Skip to content

Commit 8df6a97

Browse files
authored
Merge pull request #55 from davidbrochart/mapping
Add mapping application
2 parents 4d55dfb + 79b3d0b commit 8df6a97

File tree

1 file changed

+251
-0
lines changed

1 file changed

+251
-0
lines changed

examples/mapping.ipynb

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"from math import pow, cos, pi, radians, degrees, atan, tan, sinh, log\n",
10+
"from ipywidgets import Image, Output, IntSlider\n",
11+
"from ipycanvas import Canvas, MultiCanvas"
12+
]
13+
},
14+
{
15+
"cell_type": "code",
16+
"execution_count": null,
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"def numTiles(z):\n",
21+
" return pow(2, z)\n",
22+
"\n",
23+
"def sec(x):\n",
24+
" return 1 / cos(x)\n",
25+
"\n",
26+
"def latlon2relativeXY(lat, lon):\n",
27+
" x = (lon + 180) / 360\n",
28+
" y = (1 - log(tan(radians(lat)) + sec(radians(lat))) / pi) / 2\n",
29+
" return x,y\n",
30+
"\n",
31+
"def latlon2xy(lat, lon, z):\n",
32+
" n = numTiles(z)\n",
33+
" x, y = latlon2relativeXY(lat, lon)\n",
34+
" return n * x, n * y\n",
35+
" \n",
36+
"def tileXY(lat, lon, z):\n",
37+
" x, y = latlon2xy(lat, lon, z)\n",
38+
" return int(x), int(y)\n",
39+
"\n",
40+
"def xy2latlon(x, y, z):\n",
41+
" n = numTiles(z)\n",
42+
" relY = y / n\n",
43+
" lat = mercatorToLat(pi * (1 - 2 * relY))\n",
44+
" lon = -180 + 360 * x / n\n",
45+
" return lat, lon\n",
46+
"\n",
47+
"def mercatorToLat(mercatorY):\n",
48+
" return degrees(atan(sinh(mercatorY)))"
49+
]
50+
},
51+
{
52+
"cell_type": "code",
53+
"execution_count": null,
54+
"metadata": {},
55+
"outputs": [],
56+
"source": [
57+
"def get_tile_grid(x, y, width, height, ntiles):\n",
58+
" def get_indices(x, width, ntiles):\n",
59+
" def _(x, width, p, ntiles, xs=None, ns=None):\n",
60+
" '''p == 0: backward\n",
61+
" p == 1: forward\n",
62+
" Must be called with p=0 then p=1\n",
63+
" '''\n",
64+
" x2 = (x % 1) * 256\n",
65+
" if p == 0:\n",
66+
" xs = [width / 2 - x2]\n",
67+
" ns = [int(x)]\n",
68+
" else:\n",
69+
" x2 = 256 - x2\n",
70+
" done = False\n",
71+
" while not done:\n",
72+
" if x2 >= width / 2:\n",
73+
" # out of canvas, don't show next tile\n",
74+
" done = True\n",
75+
" else:\n",
76+
" # show (part of) next tile\n",
77+
" x2 += 256\n",
78+
" if p == 0:\n",
79+
" n1 = ns[-1] - 1\n",
80+
" x1 = xs[-1] - 256\n",
81+
" if n1 < 0:\n",
82+
" done = True\n",
83+
" else:\n",
84+
" ns.append(n1)\n",
85+
" xs.append(x1)\n",
86+
" else:\n",
87+
" n1 = ns[-1] + 1\n",
88+
" x1 = xs[-1] + 256\n",
89+
" if n1 >= ntiles:\n",
90+
" done = True\n",
91+
" else:\n",
92+
" ns.append(n1)\n",
93+
" xs.append(x1)\n",
94+
" if p == 0:\n",
95+
" xs = xs[::-1]\n",
96+
" ns = ns[::-1]\n",
97+
" return xs, ns\n",
98+
" xs, ns = _(x, width, 0, ntiles)\n",
99+
" xs, ns = _(x, width, 1, ntiles, xs, ns)\n",
100+
" return xs, ns\n",
101+
" xs, xn = get_indices(x, width, ntiles)\n",
102+
" ys, yn = get_indices(y, height, ntiles)\n",
103+
" def get_grid(xs, ys):\n",
104+
" xys = []\n",
105+
" for j in ys:\n",
106+
" xys.append([])\n",
107+
" for i in xs:\n",
108+
" xys[-1].append((i, j))\n",
109+
" return xys\n",
110+
" xys = get_grid(xs, ys)\n",
111+
" xyn = get_grid(xn, yn)\n",
112+
" return {'pix': xys, 'tile': xyn}"
113+
]
114+
},
115+
{
116+
"cell_type": "code",
117+
"execution_count": null,
118+
"metadata": {},
119+
"outputs": [],
120+
"source": [
121+
"out = Output(layout={'border': '1px solid black'})"
122+
]
123+
},
124+
{
125+
"cell_type": "code",
126+
"execution_count": null,
127+
"metadata": {},
128+
"outputs": [],
129+
"source": [
130+
"class Map(MultiCanvas):\n",
131+
" def __init__(self, height=256, width=256, zoom=0, lat=0, lon=0):\n",
132+
" super(Map, self).__init__(2, size=(height, width))\n",
133+
" \n",
134+
" self.z = zoom\n",
135+
" self.tile_nb = numTiles(zoom)\n",
136+
" self.height = height\n",
137+
" self.width = width\n",
138+
" self.dragging = False\n",
139+
" self.lat = lat\n",
140+
" self.lon = lon\n",
141+
" self.x, self.y = latlon2xy(lat, lon, zoom)\n",
142+
" \n",
143+
" self.show(lat, lon)\n",
144+
" \n",
145+
" self[1].on_mouse_down(self.mouse_down_handler)\n",
146+
" self[1].on_mouse_move(self.mouse_move_handler)\n",
147+
" self[1].on_mouse_up(self.mouse_up_handler)\n",
148+
" self[1].on_mouse_out(self.mouse_out_handler)\n",
149+
" \n",
150+
" #@out.capture()\n",
151+
" def show(self, lat=None, lon=None):\n",
152+
" if lat is None:\n",
153+
" lat = self.lat\n",
154+
" if lon is None:\n",
155+
" lon = self.lon\n",
156+
" x, y = latlon2xy(lat, lon, self.z)\n",
157+
" grid = get_tile_grid(x, y, self.width, self.height, self.tile_nb)\n",
158+
" for i, row in enumerate(grid['pix']):\n",
159+
" for j, col in enumerate(row):\n",
160+
" dx, dy = col\n",
161+
" x, y = grid['tile'][i][j]\n",
162+
" url = f'https://tile.openstreetmap.org/{self.z}/{x}/{y}.png'\n",
163+
" tile = Image.from_url(url)\n",
164+
" self[0].draw_image(tile, dx, dy)\n",
165+
" def mouse_down_handler(self, pixel_x, pixel_y):\n",
166+
" self.dragging = True\n",
167+
" self.x_mouse = pixel_x\n",
168+
" self.y_mouse = pixel_y\n",
169+
" \n",
170+
" def mouse_move_handler(self, pixel_x, pixel_y):\n",
171+
" if self.dragging:\n",
172+
" x, y, lat, lon = self.delta(pixel_x, pixel_y)\n",
173+
" self.show(lat, lon)\n",
174+
" \n",
175+
" def delta(self, pixel_x, pixel_y):\n",
176+
" dx = (pixel_x - self.x_mouse) / 256\n",
177+
" dy = (pixel_y - self.y_mouse) / 256\n",
178+
" x = self.x - dx\n",
179+
" y = self.y - dy\n",
180+
" lat, lon = xy2latlon(x, y, self.z)\n",
181+
" return x, y, lat, lon\n",
182+
" \n",
183+
" def mouse_up_handler(self, pixel_x, pixel_y):\n",
184+
" self.dragging = False\n",
185+
" self.x, self.y, self.lat, self.lon = self.delta(pixel_x, pixel_y)\n",
186+
" self.show()\n",
187+
" \n",
188+
" def mouse_out_handler(self, pixel_x, pixel_y):\n",
189+
" self.dragging = False"
190+
]
191+
},
192+
{
193+
"cell_type": "code",
194+
"execution_count": null,
195+
"metadata": {},
196+
"outputs": [],
197+
"source": [
198+
"m = Map(788, 788, 7, 49, 2)\n",
199+
"m"
200+
]
201+
},
202+
{
203+
"cell_type": "code",
204+
"execution_count": null,
205+
"metadata": {},
206+
"outputs": [],
207+
"source": [
208+
"slider = IntSlider(description='Zoom:', value=m.z, min=3, max=10, step=1)\n",
209+
"\n",
210+
"def on_slider_change(change):\n",
211+
" m.z = slider.value\n",
212+
" m.tile_nb = numTiles(m.z)\n",
213+
" m.x, m.y = latlon2xy(m.lat, m.lon, m.z)\n",
214+
" m.show()\n",
215+
"\n",
216+
"slider.observe(on_slider_change, 'value')\n",
217+
"slider"
218+
]
219+
},
220+
{
221+
"cell_type": "code",
222+
"execution_count": null,
223+
"metadata": {},
224+
"outputs": [],
225+
"source": [
226+
"out"
227+
]
228+
}
229+
],
230+
"metadata": {
231+
"kernelspec": {
232+
"display_name": "Python 3",
233+
"language": "python",
234+
"name": "python3"
235+
},
236+
"language_info": {
237+
"codemirror_mode": {
238+
"name": "ipython",
239+
"version": 3
240+
},
241+
"file_extension": ".py",
242+
"mimetype": "text/x-python",
243+
"name": "python",
244+
"nbconvert_exporter": "python",
245+
"pygments_lexer": "ipython3",
246+
"version": "3.7.3"
247+
}
248+
},
249+
"nbformat": 4,
250+
"nbformat_minor": 2
251+
}

0 commit comments

Comments
 (0)