geovizpy 0.1.4__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
geovizpy/__init__.py CHANGED
@@ -1,461 +1,7 @@
1
- import json
2
-
3
- class Geoviz:
4
- """
5
- A Python wrapper for the geoviz JavaScript library.
6
- Allows creating maps by chaining commands and rendering them to an HTML file.
7
- """
8
- def __init__(self, **kwargs):
9
- """
10
- Initialize the Geoviz object.
11
-
12
- Args:
13
- width (int): Width of the SVG.
14
- height (int): Height of the SVG.
15
- margin (list): Margins [top, right, bottom, left].
16
- domain (object): GeoJSON to define the domain.
17
- projection (string): Projection name (e.g., "mercator", "EqualEarth").
18
- zoomable (bool): If True, the map is zoomable.
19
- background (string): Background color.
20
- """
21
- self.commands = []
22
- self.commands.append({"name": "create", "args": kwargs})
23
-
24
- def _add_command(self, name, args):
25
- """Add a command to the list of commands to be executed."""
26
- self.commands.append({"name": name, "args": args})
27
- return self
28
-
29
- # Marks
30
- def outline(self, **kwargs):
31
- """
32
- Add an outline to the map (graticule sphere).
33
-
34
- Args:
35
- fill (string): Fill color.
36
- stroke (string): Stroke color.
37
- strokeWidth (number): Stroke width.
38
- """
39
- return self._add_command("outline", kwargs)
40
-
41
- def graticule(self, **kwargs):
42
- """
43
- Add a graticule to the map.
44
-
45
- Args:
46
- stroke (string): Stroke color.
47
- strokeWidth (number): Stroke width.
48
- step (list): Step [x, y] in degrees.
49
- """
50
- return self._add_command("graticule", kwargs)
51
-
52
- def path(self, **kwargs):
53
- """
54
- Draw a path (geometry) on the map.
55
-
56
- Args:
57
- datum (object): GeoJSON Feature or FeatureCollection.
58
- fill (string): Fill color.
59
- stroke (string): Stroke color.
60
- strokeWidth (number): Stroke width.
61
- """
62
- return self._add_command("path", kwargs)
63
-
64
- def header(self, **kwargs):
65
- """
66
- Add a header (title) to the map.
67
-
68
- Args:
69
- text (string): Title text.
70
- fontSize (number): Font size.
71
- fontFamily (string): Font family.
72
- fill (string): Text color.
73
- anchor (string): Text anchor ("start", "middle", "end").
74
- """
75
- return self._add_command("header", kwargs)
76
-
77
- def footer(self, **kwargs):
78
- """
79
- Add a footer (source, author) to the map.
80
-
81
- Args:
82
- text (string): Footer text.
83
- fontSize (number): Font size.
84
- fill (string): Text color.
85
- anchor (string): Text anchor.
86
- """
87
- return self._add_command("footer", kwargs)
88
-
89
- def circle(self, **kwargs):
90
- """
91
- Draw circles on the map (low-level mark).
92
- For proportional circles with legend, use prop().
93
-
94
- Args:
95
- data (object): GeoJSON FeatureCollection.
96
- r (string|number): Radius value or property name.
97
- fill (string): Fill color.
98
- stroke (string): Stroke color.
99
- tip (string|bool): Tooltip content.
100
- """
101
- return self._add_command("circle", kwargs)
102
-
103
- def square(self, **kwargs):
104
- """
105
- Draw squares on the map (low-level mark).
106
- For proportional squares with legend, use prop(symbol="square").
107
-
108
- Args:
109
- data (object): GeoJSON FeatureCollection.
110
- side (string|number): Side length or property name.
111
- fill (string): Fill color.
112
- """
113
- return self._add_command("square", kwargs)
114
-
115
- def spike(self, **kwargs):
116
- """
117
- Draw spikes on the map (low-level mark).
118
-
119
- Args:
120
- data (object): GeoJSON FeatureCollection.
121
- height (string|number): Height value or property name.
122
- width (number): Width of the spike.
123
- fill (string): Fill color.
124
- """
125
- return self._add_command("spike", kwargs)
126
-
127
- def text(self, **kwargs):
128
- """
129
- Add text labels to the map.
130
-
131
- Args:
132
- data (object): GeoJSON FeatureCollection.
133
- text (string): Property name for the text.
134
- fontSize (number): Font size.
135
- fill (string): Text color.
136
- """
137
- return self._add_command("text", kwargs)
138
-
139
- def tile(self, **kwargs):
140
- """
141
- Add a tile layer (basemap).
142
-
143
- Args:
144
- url (string): URL template or keyword (e.g., "worldStreet", "openstreetmap").
145
- opacity (number): Opacity (0 to 1).
146
- """
147
- return self._add_command("tile", kwargs)
148
-
149
- def scalebar(self, **kwargs):
150
- """
151
- Add a scale bar.
152
-
153
- Args:
154
- x (number): X position.
155
- y (number): Y position.
156
- units (string): "km" or "mi".
157
- """
158
- return self._add_command("scalebar", kwargs)
159
-
160
- def north(self, **kwargs):
161
- """
162
- Add a north arrow.
163
-
164
- Args:
165
- x (number): X position.
166
- y (number): Y position.
167
- width (number): Width of the arrow.
168
- """
169
- return self._add_command("north", kwargs)
170
-
171
- def plot(self, **kwargs):
172
- """
173
- Generic plot function.
174
-
175
- Args:
176
- type (string): Type of plot ("choro", "prop", "typo", etc.).
177
- data (object): GeoJSON data.
178
- var (string): Variable to map.
179
- """
180
- return self._add_command("plot", kwargs)
181
-
182
- def tissot(self, **kwargs):
183
- """Draw Tissot's indicatrix to visualize distortion."""
184
- return self._add_command("tissot", kwargs)
185
-
186
- def rhumbs(self, **kwargs):
187
- """Draw rhumb lines."""
188
- return self._add_command("rhumbs", kwargs)
189
-
190
- def earth(self, **kwargs):
191
- """Draw the earth (background)."""
192
- return self._add_command("earth", kwargs)
193
-
194
- def empty(self, **kwargs):
195
- """Create an empty layer."""
196
- return self._add_command("empty", kwargs)
197
-
198
- def halfcircle(self, **kwargs):
199
- """Draw half-circles."""
200
- return self._add_command("halfcircle", kwargs)
201
-
202
- def symbol(self, **kwargs):
203
- """Draw symbols."""
204
- return self._add_command("symbol", kwargs)
205
-
206
- def grid(self, **kwargs):
207
- """Draw a grid."""
208
- return self._add_command("grid", kwargs)
209
-
210
- # Plot shortcuts (sugar syntax for plot({type: ...}))
211
- def choro(self, **kwargs):
212
- """
213
- Draw a choropleth map.
214
-
215
- Args:
216
- data (object): GeoJSON FeatureCollection.
217
- var (string): Variable name containing numeric values.
218
- method (string): Classification method ('quantile', 'jenks', 'equal', etc.).
219
- nb (int): Number of classes.
220
- colors (string|list): Color palette name or list of colors.
221
- legend (bool): Whether to show the legend (default: True).
222
- leg_pos (list): Legend position [x, y].
223
- leg_title (string): Legend title.
224
- """
225
- return self._add_command("plot", {"type": "choro", **kwargs})
226
-
227
- def typo(self, **kwargs):
228
- """
229
- Draw a typology map (categorical data).
230
-
231
- Args:
232
- data (object): GeoJSON FeatureCollection.
233
- var (string): Variable name containing categories.
234
- colors (string|list): Color palette or list.
235
- legend (bool): Show legend.
236
- """
237
- return self._add_command("plot", {"type": "typo", **kwargs})
238
-
239
- def prop(self, **kwargs):
240
- """
241
- Draw a proportional symbol map.
242
-
243
- Args:
244
- data (object): GeoJSON FeatureCollection.
245
- var (string): Variable name containing numeric values.
246
- symbol (string): Symbol type ("circle", "square", "spike").
247
- k (number): Size of the largest symbol.
248
- fill (string): Fill color.
249
- legend (bool): Show legend.
250
- leg_type (string): Legend style ("nested", "separate").
251
- """
252
- return self._add_command("plot", {"type": "prop", **kwargs})
253
-
254
- def propchoro(self, **kwargs):
255
- """
256
- Draw proportional symbols colored by a choropleth variable.
257
-
258
- Args:
259
- data (object): GeoJSON FeatureCollection.
260
- var (string): Variable for symbol size.
261
- var2 (string): Variable for color.
262
- method (string): Classification method for color.
263
- colors (string|list): Color palette.
264
- """
265
- return self._add_command("plot", {"type": "propchoro", **kwargs})
266
-
267
- def proptypo(self, **kwargs):
268
- """
269
- Draw proportional symbols colored by categories.
270
-
271
- Args:
272
- data (object): GeoJSON FeatureCollection.
273
- var (string): Variable for symbol size.
274
- var2 (string): Variable for category color.
275
- """
276
- return self._add_command("plot", {"type": "proptypo", **kwargs})
277
-
278
- def picto(self, **kwargs):
279
- """Draw a pictogram map."""
280
- return self._add_command("plot", {"type": "picto", **kwargs})
281
-
282
- def bertin(self, **kwargs):
283
- """
284
- Draw a Bertin map (dots).
285
-
286
- Args:
287
- data (object): GeoJSON FeatureCollection.
288
- var (string): Variable name.
289
- n (int): Number of dots per unit.
290
- """
291
- return self._add_command("plot", {"type": "bertin", **kwargs})
292
-
293
- # Legends
294
- def legend_circles_nested(self, **kwargs):
295
- """Draw a nested circles legend."""
296
- return self._add_command("legend.circles_nested", kwargs)
297
-
298
- def legend_circles(self, **kwargs):
299
- """Draw a circles legend."""
300
- return self._add_command("legend.circles", kwargs)
301
-
302
- def legend_squares(self, **kwargs):
303
- """Draw a squares legend."""
304
- return self._add_command("legend.squares", kwargs)
305
-
306
- def legend_squares_nested(self, **kwargs):
307
- """Draw a nested squares legend."""
308
- return self._add_command("legend.squares_nested", kwargs)
309
-
310
- def legend_circles_half(self, **kwargs):
311
- """Draw a half-circles legend."""
312
- return self._add_command("legend.circles_half", kwargs)
313
-
314
- def legend_spikes(self, **kwargs):
315
- """Draw a spikes legend."""
316
- return self._add_command("legend.spikes", kwargs)
317
-
318
- def legend_mushrooms(self, **kwargs):
319
- """Draw a mushrooms legend."""
320
- return self._add_command("legend.mushrooms", kwargs)
321
-
322
- def legend_choro_vertical(self, **kwargs):
323
- """Draw a vertical choropleth legend."""
324
- return self._add_command("legend.choro_vertical", kwargs)
325
-
326
- def legend_choro_horizontal(self, **kwargs):
327
- """Draw a horizontal choropleth legend."""
328
- return self._add_command("legend.choro_horizontal", kwargs)
329
-
330
- def legend_typo_vertical(self, **kwargs):
331
- """Draw a vertical typology legend."""
332
- return self._add_command("legend.typo_vertical", kwargs)
333
-
334
- def legend_typo_horizontal(self, **kwargs):
335
- """Draw a horizontal typology legend."""
336
- return self._add_command("legend.typo_horizontal", kwargs)
337
-
338
- def legend_symbol_vertical(self, **kwargs):
339
- """Draw a vertical symbol legend."""
340
- return self._add_command("legend.symbol_vertical", kwargs)
341
-
342
- def legend_symbol_horizontal(self, **kwargs):
343
- """Draw a horizontal symbol legend."""
344
- return self._add_command("legend.symbol_horizontal", kwargs)
345
-
346
- def legend_box(self, **kwargs):
347
- """Draw a box legend."""
348
- return self._add_command("legend.box", kwargs)
349
-
350
- # Effects
351
- def effect_blur(self, **kwargs):
352
- """Apply a blur effect."""
353
- return self._add_command("effect.blur", kwargs)
354
-
355
- def effect_shadow(self, **kwargs):
356
- """Apply a shadow effect."""
357
- return self._add_command("effect.shadow", kwargs)
358
-
359
- def effect_radialGradient(self, **kwargs):
360
- """Apply a radial gradient effect."""
361
- return self._add_command("effect.radialGradient", kwargs)
362
-
363
- def effect_clipPath(self, **kwargs):
364
- """Apply a clip path effect."""
365
- return self._add_command("effect.clipPath", kwargs)
366
-
367
- def get_config(self):
368
- """Return the configuration as a JSON-compatible list of commands."""
369
- def process_args(args):
370
- new_args = {}
371
- for k, v in args.items():
372
- if v is None:
373
- continue # Skip None values
374
- if isinstance(v, str) and (v.strip().startswith("(") or v.strip().startswith("function") or "=>" in v):
375
- new_args[k] = {"__js_func__": v}
376
- elif isinstance(v, dict):
377
- new_args[k] = process_args(v)
378
- else:
379
- new_args[k] = v
380
- return new_args
381
-
382
- processed_commands = []
383
- for cmd in self.commands:
384
- processed_commands.append({"name": cmd["name"], "args": process_args(cmd["args"])})
385
-
386
- return processed_commands
387
-
388
- def to_json(self):
389
- """Return the configuration as a JSON string."""
390
- return json.dumps(self.get_config())
391
-
392
- def render_html(self, filename="map.html"):
393
- """Render the map to an HTML file."""
394
- json_commands = self.to_json()
395
-
396
- html_content = f"""
397
- <!DOCTYPE html>
398
- <html>
399
- <head>
400
- <meta charset="UTF-8" />
401
- <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Tangerine"/>
402
- <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
403
- <script src="https://cdn.jsdelivr.net/npm/geoviz@0.9.8"></script>
404
- </head>
405
- <body>
406
- <script>
407
- const commands = {json_commands};
408
- let svg;
409
-
410
- // Helper to revive functions
411
- function revive(obj) {{
412
- if (typeof obj === 'object' && obj !== null) {{
413
- if (obj.hasOwnProperty('__js_func__')) {{
414
- try {{
415
- return eval(obj['__js_func__']);
416
- }} catch (e) {{
417
- console.error("Failed to eval function:", obj['__js_func__'], e);
418
- return null;
419
- }}
420
- }} else {{
421
- for (let key in obj) {{
422
- obj[key] = revive(obj[key]);
423
- }}
424
- }}
425
- }}
426
- return obj;
427
- }}
428
-
429
- const revivedCommands = revive(commands);
1
+ """
2
+ Geovizpy: A Python wrapper for the geoviz JavaScript library.
3
+ """
430
4
 
431
- revivedCommands.forEach(cmd => {{
432
- if (cmd.name === "create") {{
433
- svg = geoviz.create(cmd.args);
434
- }} else {{
435
- const parts = cmd.name.split(".");
436
- if (parts.length === 1) {{
437
- if (svg[parts[0]]) {{
438
- svg[parts[0]](cmd.args);
439
- }} else {{
440
- console.warn("Method " + parts[0] + " not found");
441
- }}
442
- }} else if (parts.length === 2) {{
443
- if (svg[parts[0]] && svg[parts[0]][parts[1]]) {{
444
- svg[parts[0]][parts[1]](cmd.args);
445
- }} else {{
446
- console.warn("Method " + cmd.name + " not found");
447
- }}
448
- }}
449
- }}
450
- }});
5
+ from .geoviz import Geoviz
451
6
 
452
- if (svg) {{
453
- document.body.appendChild(svg.render());
454
- }}
455
- </script>
456
- </body>
457
- </html>
458
- """
459
- with open(filename, "w") as f:
460
- f.write(html_content)
461
- print(f"Map saved to {filename}")
7
+ __all__ = ["Geoviz"]
geovizpy/controls.py ADDED
@@ -0,0 +1,31 @@
1
+ """Module for interactive map controls."""
2
+
3
+ class ControlsMixin:
4
+ """Mixin class for interactive map controls."""
5
+
6
+ def add_layer_control(self, layers=None, pos=None, x=10, y=10, title="Layers"):
7
+ """
8
+ Add a collapsible layer control widget (expands on hover).
9
+
10
+ Args:
11
+ layers (list): List of layer IDs to control. If None, finds all layers with IDs.
12
+ pos (string): Predefined position ("top-right", "top-left", etc.). Overrides x, y.
13
+ x (int): X position of the control.
14
+ y (int): Y position of the control.
15
+ title (string): Title of the control panel.
16
+ """
17
+ self.layer_control_config = {"layers": layers, "pos": pos, "x": x, "y": y, "title": title}
18
+ return self
19
+
20
+ def add_export_control(self, pos=None, x=10, y=50, title="Export"):
21
+ """
22
+ Add a download button to export the map as SVG or PNG (expands on hover).
23
+
24
+ Args:
25
+ pos (string): Predefined position ("top-right", "top-left", etc.). Overrides x, y.
26
+ x (int): X position of the control.
27
+ y (int): Y position of the control.
28
+ title (string): Title of the button.
29
+ """
30
+ self.export_control_config = {"pos": pos, "x": x, "y": y, "title": title}
31
+ return self
geovizpy/effects.py ADDED
@@ -0,0 +1,48 @@
1
+ """Module for visual effects."""
2
+
3
+ class EffectsMixin:
4
+ """Mixin class for visual effects."""
5
+
6
+ def effect_blur(self, **kwargs):
7
+ """
8
+ Apply a blur effect.
9
+
10
+ Args:
11
+ id (string): ID of the effect.
12
+ stdDeviation (number): Standard deviation of the blur.
13
+ """
14
+ return self._add_command("effect.blur", kwargs)
15
+
16
+ def effect_shadow(self, **kwargs):
17
+ """
18
+ Apply a shadow effect.
19
+
20
+ Args:
21
+ id (string): ID of the effect.
22
+ dx (number): X offset.
23
+ dy (number): Y offset.
24
+ stdDeviation (number): Blur amount.
25
+ opacity (number): Opacity of the shadow.
26
+ color (string): Color of the shadow.
27
+ """
28
+ return self._add_command("effect.shadow", kwargs)
29
+
30
+ def effect_radialGradient(self, **kwargs):
31
+ """
32
+ Apply a radial gradient effect.
33
+
34
+ Args:
35
+ id (string): ID of the effect.
36
+ stops (list): List of stops (e.g., [{"offset": "0%", "color": "white"}, ...]).
37
+ """
38
+ return self._add_command("effect.radialGradient", kwargs)
39
+
40
+ def effect_clipPath(self, **kwargs):
41
+ """
42
+ Apply a clip path effect.
43
+
44
+ Args:
45
+ id (string): ID of the effect.
46
+ datum (object): GeoJSON to use as clip path.
47
+ """
48
+ return self._add_command("effect.clipPath", kwargs)
geovizpy/geoviz.py ADDED
@@ -0,0 +1,43 @@
1
+ """Main Geoviz class that assembles all modules."""
2
+
3
+ from .marks import MarksMixin
4
+ from .plots import PlotsMixin
5
+ from .legends import LegendsMixin
6
+ from .effects import EffectsMixin
7
+ from .controls import ControlsMixin
8
+ from .renderer import RendererMixin
9
+
10
+ class Geoviz(
11
+ MarksMixin,
12
+ PlotsMixin,
13
+ LegendsMixin,
14
+ EffectsMixin,
15
+ ControlsMixin,
16
+ RendererMixin
17
+ ):
18
+ """
19
+ A Python wrapper for the geoviz JavaScript library.
20
+ Allows creating maps by chaining commands and rendering them to an HTML file.
21
+ """
22
+ def __init__(self, **kwargs):
23
+ """
24
+ Initialize the Geoviz object.
25
+
26
+ Args:
27
+ width (int): Width of the SVG.
28
+ height (int): Height of the SVG.
29
+ margin (list): Margins [top, right, bottom, left].
30
+ domain (object): GeoJSON to define the domain.
31
+ projection (string): Projection name (e.g., "mercator", "EqualEarth").
32
+ zoomable (bool): If True, the map is zoomable.
33
+ background (string): Background color.
34
+ """
35
+ self.commands = []
36
+ self.commands.append({"name": "create", "args": kwargs})
37
+ self.layer_control_config = None
38
+ self.export_control_config = None
39
+
40
+ def _add_command(self, name, args):
41
+ """Add a command to the list of commands to be executed."""
42
+ self.commands.append({"name": name, "args": args})
43
+ return self
geovizpy/legends.py ADDED
@@ -0,0 +1,60 @@
1
+ """Module for map legends."""
2
+
3
+ class LegendsMixin:
4
+ """Mixin class for map legends."""
5
+
6
+ def legend_circles_nested(self, **kwargs):
7
+ """Draw a nested circles legend."""
8
+ return self._add_command("legend.circles_nested", kwargs)
9
+
10
+ def legend_circles(self, **kwargs):
11
+ """Draw a circles legend."""
12
+ return self._add_command("legend.circles", kwargs)
13
+
14
+ def legend_squares(self, **kwargs):
15
+ """Draw a squares legend."""
16
+ return self._add_command("legend.squares", kwargs)
17
+
18
+ def legend_squares_nested(self, **kwargs):
19
+ """Draw a nested squares legend."""
20
+ return self._add_command("legend.squares_nested", kwargs)
21
+
22
+ def legend_circles_half(self, **kwargs):
23
+ """Draw a half-circles legend."""
24
+ return self._add_command("legend.circles_half", kwargs)
25
+
26
+ def legend_spikes(self, **kwargs):
27
+ """Draw a spikes legend."""
28
+ return self._add_command("legend.spikes", kwargs)
29
+
30
+ def legend_mushrooms(self, **kwargs):
31
+ """Draw a mushrooms legend."""
32
+ return self._add_command("legend.mushrooms", kwargs)
33
+
34
+ def legend_choro_vertical(self, **kwargs):
35
+ """Draw a vertical choropleth legend."""
36
+ return self._add_command("legend.choro_vertical", kwargs)
37
+
38
+ def legend_choro_horizontal(self, **kwargs):
39
+ """Draw a horizontal choropleth legend."""
40
+ return self._add_command("legend.choro_horizontal", kwargs)
41
+
42
+ def legend_typo_vertical(self, **kwargs):
43
+ """Draw a vertical typology legend."""
44
+ return self._add_command("legend.typo_vertical", kwargs)
45
+
46
+ def legend_typo_horizontal(self, **kwargs):
47
+ """Draw a horizontal typology legend."""
48
+ return self._add_command("legend.typo_horizontal", kwargs)
49
+
50
+ def legend_symbol_vertical(self, **kwargs):
51
+ """Draw a vertical symbol legend."""
52
+ return self._add_command("legend.symbol_vertical", kwargs)
53
+
54
+ def legend_symbol_horizontal(self, **kwargs):
55
+ """Draw a horizontal symbol legend."""
56
+ return self._add_command("legend.symbol_horizontal", kwargs)
57
+
58
+ def legend_box(self, **kwargs):
59
+ """Draw a box legend."""
60
+ return self._add_command("legend.box", kwargs)