geovizpy 0.1.6__tar.gz → 0.1.8__tar.gz

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.
Files changed (33) hide show
  1. {geovizpy-0.1.6 → geovizpy-0.1.8}/PKG-INFO +2 -8
  2. geovizpy-0.1.8/docs/source/conf.py +32 -0
  3. geovizpy-0.1.8/examples/advanced_plots.py +82 -0
  4. geovizpy-0.1.8/examples/bubble.py +46 -0
  5. geovizpy-0.1.8/examples/choropleth.py +45 -0
  6. geovizpy-0.1.8/examples/choropleth_effect.py +62 -0
  7. geovizpy-0.1.8/examples/json_export.py +37 -0
  8. geovizpy-0.1.8/examples/labels.py +43 -0
  9. geovizpy-0.1.8/examples/layer_control.py +70 -0
  10. geovizpy-0.1.8/examples/lines.py +74 -0
  11. geovizpy-0.1.8/examples/reactive.py +37 -0
  12. geovizpy-0.1.8/examples/simple.py +38 -0
  13. geovizpy-0.1.8/examples/tiles.py +47 -0
  14. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/controls.py +20 -0
  15. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/geoviz.py +1 -0
  16. geovizpy-0.1.8/geovizpy/insets.py +0 -0
  17. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/renderer.py +127 -4
  18. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy.egg-info/PKG-INFO +2 -8
  19. geovizpy-0.1.8/geovizpy.egg-info/SOURCES.txt +29 -0
  20. geovizpy-0.1.8/geovizpy.egg-info/top_level.txt +4 -0
  21. geovizpy-0.1.8/pyproject.toml +26 -0
  22. {geovizpy-0.1.6 → geovizpy-0.1.8}/setup.py +1 -1
  23. geovizpy-0.1.6/geovizpy.egg-info/SOURCES.txt +0 -15
  24. geovizpy-0.1.6/geovizpy.egg-info/top_level.txt +0 -1
  25. {geovizpy-0.1.6 → geovizpy-0.1.8}/README.md +0 -0
  26. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/__init__.py +0 -0
  27. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/effects.py +0 -0
  28. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/legends.py +0 -0
  29. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/marks.py +0 -0
  30. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy/plots.py +0 -0
  31. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy.egg-info/dependency_links.txt +0 -0
  32. {geovizpy-0.1.6 → geovizpy-0.1.8}/geovizpy.egg-info/requires.txt +0 -0
  33. {geovizpy-0.1.6 → geovizpy-0.1.8}/setup.cfg +0 -0
@@ -1,8 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geovizpy
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: A Python wrapper for the geoviz JavaScript library
5
5
  Author: fbxyz
6
+ License: MIT
6
7
  Project-URL: Source, https://codeberg.org/fbxyz/geovizpy
7
8
  Classifier: Programming Language :: Python :: 3
8
9
  Classifier: License :: OSI Approved :: MIT License
@@ -11,14 +12,7 @@ Requires-Python: >=3.6
11
12
  Description-Content-Type: text/markdown
12
13
  Provides-Extra: export
13
14
  Requires-Dist: playwright; extra == "export"
14
- Dynamic: author
15
- Dynamic: classifier
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: project-url
19
- Dynamic: provides-extra
20
15
  Dynamic: requires-python
21
- Dynamic: summary
22
16
 
23
17
  # geovizpy
24
18
 
@@ -0,0 +1,32 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ import os
3
+ import sys
4
+ sys.path.insert(0, os.path.abspath('../../'))
5
+
6
+ project = 'geovizpy'
7
+ copyright = '2026, fbxyz'
8
+ author = 'fbxyz'
9
+ release = '0.1.3'
10
+
11
+ # -- General configuration ---------------------------------------------------
12
+ extensions = [
13
+ 'sphinx.ext.autodoc',
14
+ 'sphinx.ext.napoleon',
15
+ 'sphinx.ext.viewcode',
16
+ 'sphinx.ext.githubpages',
17
+ ]
18
+
19
+ templates_path = ['_templates']
20
+ exclude_patterns = []
21
+
22
+ # -- Options for HTML output -------------------------------------------------
23
+ try:
24
+ import sphinx_rtd_theme
25
+ html_theme = 'sphinx_rtd_theme'
26
+ except ImportError:
27
+ html_theme = 'alabaster'
28
+
29
+ html_static_path = ['_static']
30
+
31
+ # -- Options for autodoc -----------------------------------------------------
32
+ autodoc_member_order = 'bysource'
@@ -0,0 +1,82 @@
1
+ """Example script demonstrating advanced plot types."""
2
+
3
+ import json
4
+ import os
5
+ import random
6
+ from geovizpy import Geoviz
7
+
8
+ # Define output directory
9
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
10
+ os.makedirs(output_dir, exist_ok=True)
11
+
12
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
13
+ try:
14
+ with open(data_path) as f:
15
+ world_data = json.load(f)
16
+ except FileNotFoundError:
17
+ print(f"{data_path} not found.")
18
+ world_data = {}
19
+
20
+ continents = ["Africa", "Asia", "Europe", "North America", "South America", "Oceania"]
21
+ symbols_list = ["star", "circle", "cross", "diamond", "square", "triangle"]
22
+
23
+ for f in world_data.get("features", []):
24
+ f["properties"]["random_continent"] = random.choice(continents)
25
+ f["properties"]["random_symbol"] = random.choice(symbols_list)
26
+
27
+ # 1. Typology Map
28
+ viz1 = Geoviz(projection="EqualEarth", width=800)
29
+ viz1.outline()
30
+ viz1.graticule(stroke="white", strokeWidth=0.4)
31
+
32
+ viz1.typo(
33
+ data=world_data,
34
+ var="random_continent",
35
+ colors="Pastel",
36
+ strokeWidth=0.5,
37
+ leg_title="Random Continent",
38
+ leg_pos=[10, 200]
39
+ )
40
+ viz1.header(text="Typology Map (Random Data)", fontSize=20)
41
+ viz1.render_html(os.path.join(output_dir, "advanced_typo.html"))
42
+
43
+ # 2. Proportional Symbols with Typology
44
+ viz2 = Geoviz(projection="EqualEarth", width=800)
45
+ viz2.outline()
46
+ viz2.graticule()
47
+ viz2.path(datum=world_data, fill="#eee")
48
+
49
+ viz2.proptypo(
50
+ data=world_data,
51
+ var1="pop",
52
+ var2="random_continent",
53
+ k=50,
54
+ colors="Set1",
55
+ leg1_title="Population",
56
+ leg2_title="Continent",
57
+ leg1_pos=[10, 300],
58
+ leg2_pos=[10, 150]
59
+ )
60
+ viz2.header(text="Proportional Symbols + Typology", fontSize=20)
61
+ viz2.render_html(os.path.join(output_dir, "advanced_proptypo.html"))
62
+
63
+ # 3. Symbol Map
64
+ viz3 = Geoviz(projection="EqualEarth", width=800)
65
+ viz3.outline()
66
+ viz3.graticule()
67
+ viz3.path(datum=world_data, fill="#ddd")
68
+
69
+ viz3.picto(
70
+ data=world_data,
71
+ var="random_symbol",
72
+ symbols=["star", "circle", "cross", "diamond", "square", "triangle"],
73
+ colors="black",
74
+ fill="red",
75
+ stroke="white",
76
+ strokeWidth=1,
77
+ k=15,
78
+ leg_title="Random Symbols",
79
+ leg_pos=[10, 200]
80
+ )
81
+ viz3.header(text="Symbol Map (Picto)", fontSize=20)
82
+ viz3.render_html(os.path.join(output_dir, "advanced_symbol.html"))
@@ -0,0 +1,46 @@
1
+ """Example script for creating a proportional symbol map (bubble map)."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"{data_path} not found.")
17
+ world_data = {}
18
+
19
+ viz = Geoviz(projection="EqualEarth", zoomable=True)
20
+ viz.outline()
21
+ viz.graticule(stroke="white", strokeWidth=0.4)
22
+ viz.path(datum=world_data, fill="white", fillOpacity=0.4)
23
+
24
+ # Use viz.prop instead of viz.circle to get automatic legend handling
25
+ viz.prop(
26
+ data=world_data,
27
+ var="pop", # 'var' is used instead of 'r' in viz.prop
28
+ fill="#f07d75",
29
+ tip="(d) => `${d.properties.NAMEen}\n${d.properties.pop / 1000} thousands inh.`",
30
+ leg_type="separate",
31
+ leg_title="Population",
32
+ leg_pos=[30, 30], # Added position to make sure it's visible
33
+ leg_title_fontSize=15
34
+ )
35
+
36
+ pops = [f["properties"]["pop"] for f in world_data.get("features", []) if "pop" in f["properties"]]
37
+
38
+ viz.header(
39
+ fontSize=30,
40
+ text="A World Map With Bubbles",
41
+ fill="#267A8A",
42
+ fontWeight="bold",
43
+ fontFamily="Tangerine"
44
+ )
45
+
46
+ viz.render_html(os.path.join(output_dir, "bubble.html"))
@@ -0,0 +1,45 @@
1
+ """Example script for a choropleth map."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"{data_path} not found.")
17
+ world_data = {}
18
+
19
+ viz = Geoviz(projection="EqualEarth")
20
+ viz.outline()
21
+ viz.graticule(stroke="white", step=30, strokeWidth=1.2)
22
+
23
+ viz.choro(
24
+ data=world_data,
25
+ var="gdppc",
26
+ strokeWidth=0.3,
27
+ tip=True,
28
+ leg_type="horizontal",
29
+ leg_title="GDP per capita",
30
+ leg_subtitle="($ per inh.)",
31
+ leg_note="Source: worldbank, 2020",
32
+ leg_pos=[410, 370],
33
+ leg_values_round=0,
34
+ leg_missing_text="Missing values"
35
+ )
36
+
37
+ viz.header(
38
+ fontSize=30,
39
+ text="A Choropleth World Map",
40
+ fill="#267A8A",
41
+ fontWeight="bold",
42
+ fontFamily="Tangerine"
43
+ )
44
+
45
+ viz.render_html(os.path.join(output_dir, "choropleth.html"))
@@ -0,0 +1,62 @@
1
+ """Example script demonstrating visual effects (shadow)."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"{data_path} not found.")
17
+ world_data = {}
18
+
19
+ brazil = {
20
+ "type": "FeatureCollection",
21
+ "features": [f for f in world_data.get("features", []) if f["properties"].get("NAMEen") == "Brazil"]
22
+ }
23
+
24
+ viz = Geoviz(projection="EqualEarth")
25
+
26
+ viz.effect_shadow(id="myshadow", dx=10, dy=10, opacity=0.5)
27
+
28
+ viz.outline()
29
+ viz.graticule(stroke="white", step=30, strokeWidth=1.2)
30
+
31
+ viz.choro(
32
+ data=world_data,
33
+ var="gdppc",
34
+ strokeWidth=0.3,
35
+ tip=True,
36
+ leg_type="horizontal",
37
+ leg_title="GDP per capita",
38
+ leg_subtitle="($ per inh.)",
39
+ leg_note="Source: worldbank, 2020",
40
+ leg_pos=[410, 370],
41
+ leg_values_round=0,
42
+ leg_missing_text="Missing values"
43
+ )
44
+
45
+ if brazil["features"]:
46
+ viz.path(
47
+ data=brazil,
48
+ fill="none",
49
+ stroke="red",
50
+ strokeWidth=2,
51
+ filter="url(#myshadow)"
52
+ )
53
+
54
+ viz.header(
55
+ fontSize=30,
56
+ text="Choropleth with Shadow Effect on Brazil",
57
+ fill="#267A8A",
58
+ fontWeight="bold",
59
+ fontFamily="Tangerine"
60
+ )
61
+
62
+ viz.render_html(os.path.join(output_dir, "choropleth_effect.html"))
@@ -0,0 +1,37 @@
1
+ """Example script for exporting the map configuration to JSON."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"{data_path} not found.")
17
+ world_data = {}
18
+
19
+ viz = Geoviz(projection="EqualEarth")
20
+ viz.outline()
21
+ viz.graticule(stroke="white", strokeWidth=0.4)
22
+ viz.choro(
23
+ data=world_data,
24
+ var="gdppc",
25
+ strokeWidth=0.3,
26
+ tip=True,
27
+ leg_title="GDP per capita"
28
+ )
29
+ viz.header(text="Map from JSON Config", fontSize=30)
30
+
31
+ config_json = viz.to_json()
32
+ print("Configuration JSON (first 500 chars):")
33
+ print(config_json[:500] + "...")
34
+
35
+ with open(os.path.join(output_dir, "map_config.json"), "w") as f:
36
+ f.write(config_json)
37
+ print(f"\nConfiguration saved to {os.path.join(output_dir, 'map_config.json')}")
@@ -0,0 +1,43 @@
1
+ """Example script for adding text labels to a map."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"{data_path} not found.")
17
+ world_data = {}
18
+
19
+ countries_to_label = ["Brazil", "China", "United States", "France", "South Africa", "Australia"]
20
+ labeled_features = [f for f in world_data.get("features", []) if f["properties"].get("NAMEen") in countries_to_label]
21
+ labeled_data = {"type": "FeatureCollection", "features": labeled_features}
22
+
23
+ viz = Geoviz(projection="EqualEarth")
24
+ viz.outline()
25
+ viz.graticule(stroke="white", strokeWidth=0.4)
26
+
27
+ viz.path(datum=world_data, fill="#e0e0e0", stroke="white")
28
+
29
+ viz.text(
30
+ data=labeled_data,
31
+ text="NAMEen",
32
+ fill="#333",
33
+ fontSize=14,
34
+ fontWeight="bold",
35
+ stroke="white",
36
+ strokeWidth=3,
37
+ paintOrder="stroke"
38
+ )
39
+
40
+
41
+
42
+ viz.header(text="Map with Labels", fontSize=20)
43
+ viz.render_html(os.path.join(output_dir, "labels.html"))
@@ -0,0 +1,70 @@
1
+ """Example script for layer control and export functionality."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ # Load world data
12
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
13
+ try:
14
+ with open(data_path) as f:
15
+ world_data = json.load(f)
16
+ except FileNotFoundError:
17
+ print(f"{data_path} not found.")
18
+ world_data = {}
19
+
20
+ # Create a map
21
+ viz = Geoviz(projection="EqualEarth", zoomable=True)
22
+ viz.outline(id="outline")
23
+ viz.graticule(stroke="white", strokeWidth=0.4, id="graticule")
24
+
25
+ # Add a choropleth layer with an ID and legend position
26
+ viz.choro(
27
+ data=world_data,
28
+ var="gdppc",
29
+ colors="Reds",
30
+ id="choropleth_gdp",
31
+ legend=True,
32
+ leg_title="GDP per Capita",
33
+ leg_pos=[10, 100] # Position for the choro legend
34
+ )
35
+
36
+ # Add a proportional symbol layer with an ID and legend position
37
+ viz.prop(
38
+ data=world_data,
39
+ var="pop",
40
+ fill="#4f8a8b",
41
+ stroke="white",
42
+ id="prop_population",
43
+ legend=True,
44
+ leg_title="Population",
45
+ leg_pos=[10, 250] # Position for the prop legend
46
+ )
47
+
48
+ # Add the layer control widget (hover to expand)
49
+ viz.add_layer_control(
50
+ layers=["choropleth_gdp", "prop_population", "graticule"],
51
+ title="Layers",
52
+ x=10,
53
+ y=10
54
+ )
55
+
56
+ # Add the export control widget (hover to expand)
57
+ viz.add_export_control(
58
+ pos="top-right" # Or use x, y
59
+ )
60
+
61
+ # Add opacity control for multiple layers
62
+ viz.add_opacity_control(
63
+ layers=["choropleth_gdp", "prop_population"],
64
+ title="Layers Opacity",
65
+ x=10,
66
+ y=90
67
+ )
68
+
69
+ viz.header(text="Map with Layer & Export Controls", fontSize=20)
70
+ viz.render_html(os.path.join(output_dir, "layer_control_map.html"))
@@ -0,0 +1,74 @@
1
+ """Example script demonstrating how to draw lines on a map."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"{data_path} not found.")
17
+ world_data = {}
18
+
19
+ lines_data = {
20
+ "type": "FeatureCollection",
21
+ "features": [
22
+ {
23
+ "type": "Feature",
24
+ "properties": {"destination": "New York", "type": "Long-haul"},
25
+ "geometry": {
26
+ "type": "LineString",
27
+ "coordinates": [[2.35, 48.85], [-74.00, 40.71]]
28
+ }
29
+ },
30
+ {
31
+ "type": "Feature",
32
+ "properties": {"destination": "Beijing", "type": "Long-haul"},
33
+ "geometry": {
34
+ "type": "LineString",
35
+ "coordinates": [[2.35, 48.85], [116.40, 39.90]]
36
+ }
37
+ },
38
+ {
39
+ "type": "Feature",
40
+ "properties": {"destination": "Cape Town", "type": "Long-haul"},
41
+ "geometry": {
42
+ "type": "LineString",
43
+ "coordinates": [[2.35, 48.85], [18.42, -33.92]]
44
+ }
45
+ },
46
+ {
47
+ "type": "Feature",
48
+ "properties": {"destination": "Moscow", "type": "Medium-haul"},
49
+ "geometry": {
50
+ "type": "LineString",
51
+ "coordinates": [[2.35, 48.85], [37.61, 55.75]]
52
+ }
53
+ }
54
+ ]
55
+ }
56
+
57
+ viz = Geoviz(projection="EqualEarth")
58
+ viz.outline()
59
+ viz.graticule(stroke="white", strokeWidth=0.4)
60
+
61
+ viz.path(datum=world_data, fill="#e0e0e0", stroke="white")
62
+
63
+ viz.typo(
64
+ data=lines_data,
65
+ var="destination",
66
+ colors="Set1",
67
+ strokeWidth=2,
68
+ fill="none",
69
+ leg_title="Routes from Paris",
70
+ leg_pos=[10, 250]
71
+ )
72
+
73
+ viz.header(text="Map with Lines (Routes)", fontSize=20)
74
+ viz.render_html(os.path.join(output_dir, "lines.html"))
@@ -0,0 +1,37 @@
1
+ """Example script for a reactive map (zoomable)."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"{data_path} not found.")
17
+ world_data = {}
18
+
19
+ viz = Geoviz(projection="polar", width=700, zoomable=True)
20
+ viz.outline()
21
+ viz.graticule(stroke="white", strokeWidth=0.4)
22
+ viz.path(datum=world_data, fill="white", fillOpacity=0.3)
23
+
24
+ viz.prop(
25
+ id="bubbles",
26
+ symbol="circle",
27
+ data=world_data,
28
+ var="pop",
29
+ fill="#F13C47",
30
+ dodge=False,
31
+ k=50,
32
+ leg_title="Population",
33
+ tip=True
34
+ )
35
+
36
+ print("Generating a static snapshot of the map.")
37
+ viz.render_html(os.path.join(output_dir, "reactive_snapshot.html"))
@@ -0,0 +1,38 @@
1
+ """Example script for a simple world map."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ # Load geojson
12
+ # Assuming running from repo root or examples folder
13
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
14
+ try:
15
+ with open(data_path) as f:
16
+ world_data = json.load(f)
17
+ except FileNotFoundError:
18
+ print(f"{data_path} not found.")
19
+ world_data = {}
20
+
21
+ viz = Geoviz(projection="EqualEarth")
22
+ viz.outline(fill="#267A8A")
23
+ viz.graticule(stroke="white", strokeWidth=0.4)
24
+ viz.path(
25
+ data=world_data,
26
+ fill="#F8D993",
27
+ stroke="#ada9a6",
28
+ strokeWidth=0.5,
29
+ tip="$NAMEen"
30
+ )
31
+ viz.header(
32
+ fontSize=30,
33
+ text="A Simple World Map",
34
+ fill="#267A8A",
35
+ fontWeight="bold",
36
+ fontFamily="Tangerine"
37
+ )
38
+ viz.render_html(os.path.join(output_dir, "simple.html"))
@@ -0,0 +1,47 @@
1
+ """Example script demonstrating how to use tile layers."""
2
+
3
+ import json
4
+ import os
5
+ from geovizpy import Geoviz
6
+
7
+ # Define output directory
8
+ output_dir = os.path.join(os.path.dirname(__file__), "html")
9
+ os.makedirs(output_dir, exist_ok=True)
10
+
11
+ data_path = os.path.join(os.path.dirname(__file__), "data", "world.json")
12
+ try:
13
+ with open(data_path) as f:
14
+ world_data = json.load(f)
15
+ except FileNotFoundError:
16
+ print(f"Data file not found: {data_path}")
17
+ world_data = {}
18
+
19
+ viz = Geoviz(projection="mercator", zoomable=True)
20
+
21
+ # Use the keyword for the tile layer, as per geoviz.js documentation
22
+ viz.tile(url="worldStreet")
23
+
24
+ viz.graticule(stroke="white", strokeWidth=0.4)
25
+
26
+ # This line might fail if world_data is empty
27
+ if world_data:
28
+ viz.prop(
29
+ data=world_data,
30
+ var="pop",
31
+ fill="#f07d75",
32
+ tip="$NAMEen",
33
+ leg_title="Population",
34
+ leg_frame=True,
35
+ leg_pos=[30, 30]
36
+ )
37
+
38
+ viz.text(
39
+ pos=[0,0],
40
+ text="Source: INSEE 2022",
41
+ fontSize=10,
42
+ fill="#666",
43
+ anchor="end"
44
+ )
45
+
46
+
47
+ viz.render_html(os.path.join(output_dir, "tiles.html"))
@@ -29,3 +29,23 @@ class ControlsMixin:
29
29
  """
30
30
  self.export_control_config = {"pos": pos, "x": x, "y": y, "title": title}
31
31
  return self
32
+
33
+ def add_opacity_control(self, layers, pos=None, x=10, y=90, title="Opacity"):
34
+ """
35
+ Add a slider to control the opacity of one or multiple layers.
36
+
37
+ Args:
38
+ layers (string or list): The ID(s) of the layer(s) to control.
39
+ pos (string): Predefined position ("top-right", "top-left", etc.). Overrides x, y.
40
+ x (int): X position of the control.
41
+ y (int): Y position of the control.
42
+ title (string): Title of the control.
43
+ """
44
+ if not hasattr(self, "opacity_control_configs"):
45
+ self.opacity_control_configs = []
46
+
47
+ if isinstance(layers, str):
48
+ layers = [layers]
49
+
50
+ self.opacity_control_configs.append({"layers": layers, "pos": pos, "x": x, "y": y, "title": title})
51
+ return self
@@ -36,6 +36,7 @@ class Geoviz(
36
36
  self.commands.append({"name": "create", "args": kwargs})
37
37
  self.layer_control_config = None
38
38
  self.export_control_config = None
39
+ self.opacity_control_configs = []
39
40
 
40
41
  def _add_command(self, name, args):
41
42
  """Add a command to the list of commands to be executed."""
File without changes
@@ -18,7 +18,7 @@ class RendererMixin:
18
18
  for k, v in args.items():
19
19
  if v is None:
20
20
  continue
21
- if isinstance(v, str) and (v.strip().startswith("(") or v.strip().startswith("function") or "=>" in v):
21
+ if isinstance(v, str) and (v.strip().startswith("(") or v.strip().startswith("function") or "=>" in v or v.strip().startswith("d3.")):
22
22
  new_args[k] = {"__js_func__": v}
23
23
  elif isinstance(v, dict):
24
24
  new_args[k] = process_args(v)
@@ -42,6 +42,7 @@ class RendererMixin:
42
42
 
43
43
  layer_control_js = self._get_layer_control_js()
44
44
  export_control_js = self._get_export_control_js()
45
+ opacity_control_js = self._get_opacity_control_js()
45
46
 
46
47
  return f"""
47
48
  <!DOCTYPE html>
@@ -58,6 +59,7 @@ class RendererMixin:
58
59
  </style>
59
60
  </head>
60
61
  <body>
62
+ <div id="geoviz-container" style="position: relative; display: inline-block;"></div>
61
63
  <script>
62
64
  const commands = {json_commands};
63
65
  let svg;
@@ -105,11 +107,12 @@ class RendererMixin:
105
107
  }});
106
108
 
107
109
  if (svg) {{
108
- document.body.appendChild(svg.render());
110
+ document.getElementById("geoviz-container").appendChild(svg.render());
109
111
  }}
110
112
 
111
113
  {layer_control_js}
112
114
  {export_control_js}
115
+ {opacity_control_js}
113
116
  </script>
114
117
  </body>
115
118
  </html>
@@ -335,7 +338,7 @@ if __name__ == "__main__":
335
338
  if (count > 0) {{
336
339
  wrapper.appendChild(button);
337
340
  wrapper.appendChild(panel);
338
- document.body.appendChild(wrapper);
341
+ document.getElementById("geoviz-container").appendChild(wrapper);
339
342
  }}
340
343
  }}
341
344
  setTimeout(createLayerControl, 100);
@@ -415,7 +418,127 @@ if __name__ == "__main__":
415
418
  panel.appendChild(btnPNG);
416
419
  wrapper.appendChild(button);
417
420
  wrapper.appendChild(panel);
418
- document.body.appendChild(wrapper);
421
+ document.getElementById("geoviz-container").appendChild(wrapper);
419
422
  }}
420
423
  setTimeout(createExportControl, 100);
421
424
  """
425
+
426
+ def _get_opacity_control_js(self):
427
+ if not hasattr(self, "opacity_control_configs") or not self.opacity_control_configs:
428
+ return ""
429
+
430
+ js_code = ""
431
+ for config in self.opacity_control_configs:
432
+ config_json = json.dumps(config)
433
+ js_code += f"""
434
+ (function() {{
435
+ const opacityConfig = {config_json};
436
+
437
+ function createOpacityControl() {{
438
+ const wrapper = document.createElement("div");
439
+ wrapper.style.position = "absolute";
440
+ wrapper.style.zIndex = "1000";
441
+
442
+ const button = document.createElement("div");
443
+ button.innerHTML = `<svg width="24" height="24" viewBox="0 0 24 24" fill="#333"><path d="M17.66 8L12 2.35 6.34 8C4.78 9.56 4 11.64 4 13.64s.78 4.11 2.34 5.66 3.61 2.35 5.66 2.35 4.1-.79 5.66-2.35S20 15.64 20 13.64 19.22 9.56 17.66 8zM6 14c.01-2 .62-3.27 1.76-4.4L12 5.27l4.24 4.38C17.38 10.77 17.99 12 18 14H6z"/></svg>`;
444
+ button.style.width = "32px";
445
+ button.style.height = "32px";
446
+ button.style.cursor = "pointer";
447
+ button.style.border = "1px solid #ccc";
448
+ button.style.borderRadius = "4px";
449
+ button.style.backgroundColor = "white";
450
+ button.style.display = "flex";
451
+ button.style.alignItems = "center";
452
+ button.style.justifyContent = "center";
453
+ button.style.boxShadow = "0 1px 3px rgba(0,0,0,0.2)";
454
+
455
+ const panel = document.createElement("div");
456
+ panel.style.display = "none";
457
+ panel.style.backgroundColor = "white";
458
+ panel.style.padding = "10px";
459
+ panel.style.border = "1px solid #ccc";
460
+ panel.style.borderRadius = "5px";
461
+ panel.style.fontFamily = "sans-serif";
462
+ panel.style.fontSize = "12px";
463
+ panel.style.boxShadow = "0 2px 4px rgba(0,0,0,0.2)";
464
+ panel.style.marginTop = "5px";
465
+ panel.style.minWidth = "150px";
466
+
467
+ wrapper.addEventListener("mouseenter", () => panel.style.display = "block");
468
+ wrapper.addEventListener("mouseleave", () => panel.style.display = "none");
469
+
470
+ if (opacityConfig.pos === "top-right") {{
471
+ wrapper.style.top = "90px"; wrapper.style.right = "10px";
472
+ }} else if (opacityConfig.pos === "bottom-right") {{
473
+ wrapper.style.bottom = "90px"; wrapper.style.right = "10px";
474
+ }} else {{
475
+ wrapper.style.top = `${{opacityConfig.y}}px`;
476
+ wrapper.style.left = `${{opacityConfig.x}}px`;
477
+ }}
478
+
479
+ const title = document.createElement("div");
480
+ title.innerText = opacityConfig.title;
481
+ title.style.fontWeight = "bold";
482
+ title.style.marginBottom = "8px";
483
+ title.style.borderBottom = "1px solid #eee";
484
+ title.style.paddingBottom = "5px";
485
+ panel.appendChild(title);
486
+
487
+ opacityConfig.layers.forEach(layerId => {{
488
+ const row = document.createElement("div");
489
+ row.style.marginBottom = "10px";
490
+
491
+ const label = document.createElement("div");
492
+ label.innerText = layerId;
493
+ label.style.fontSize = "11px";
494
+ label.style.marginBottom = "2px";
495
+ label.style.color = "#555";
496
+
497
+ const sliderContainer = document.createElement("div");
498
+ sliderContainer.style.display = "flex";
499
+ sliderContainer.style.alignItems = "center";
500
+
501
+ const slider = document.createElement("input");
502
+ slider.type = "range";
503
+ slider.min = "0";
504
+ slider.max = "1";
505
+ slider.step = "0.1";
506
+ slider.value = "1"; // Default value
507
+ slider.style.width = "100%";
508
+ slider.style.cursor = "pointer";
509
+
510
+ // Set initial slider value from layer's current opacity
511
+ const layerNodeForOpacity = svg.select("#" + layerId);
512
+ if (!layerNodeForOpacity.empty()) {{
513
+ const firstShape = layerNodeForOpacity.select("path, circle, rect");
514
+ if (!firstShape.empty()) {{
515
+ slider.value = firstShape.style("fill-opacity");
516
+ }}
517
+ }}
518
+
519
+ slider.addEventListener("input", (e) => {{
520
+ const val = e.target.value;
521
+ const layer = svg.select("#" + layerId);
522
+ if (!layer.empty()) {{
523
+ layer.selectAll("path, circle, rect").style("fill-opacity", val);
524
+ }}
525
+ const legLayer = svg.select("#leg_" + layerId);
526
+ if (!legLayer.empty()) {{
527
+ legLayer.selectAll("path, circle, rect").style("fill-opacity", val);
528
+ }}
529
+ }});
530
+
531
+ sliderContainer.appendChild(slider);
532
+ row.appendChild(label);
533
+ row.appendChild(sliderContainer);
534
+ panel.appendChild(row);
535
+ }});
536
+
537
+ wrapper.appendChild(button);
538
+ wrapper.appendChild(panel);
539
+ document.getElementById("geoviz-container").appendChild(wrapper);
540
+ }}
541
+ setTimeout(createOpacityControl, 100);
542
+ }})();
543
+ """
544
+ return js_code
@@ -1,8 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geovizpy
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: A Python wrapper for the geoviz JavaScript library
5
5
  Author: fbxyz
6
+ License: MIT
6
7
  Project-URL: Source, https://codeberg.org/fbxyz/geovizpy
7
8
  Classifier: Programming Language :: Python :: 3
8
9
  Classifier: License :: OSI Approved :: MIT License
@@ -11,14 +12,7 @@ Requires-Python: >=3.6
11
12
  Description-Content-Type: text/markdown
12
13
  Provides-Extra: export
13
14
  Requires-Dist: playwright; extra == "export"
14
- Dynamic: author
15
- Dynamic: classifier
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: project-url
19
- Dynamic: provides-extra
20
15
  Dynamic: requires-python
21
- Dynamic: summary
22
16
 
23
17
  # geovizpy
24
18
 
@@ -0,0 +1,29 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ docs/source/conf.py
5
+ examples/advanced_plots.py
6
+ examples/bubble.py
7
+ examples/choropleth.py
8
+ examples/choropleth_effect.py
9
+ examples/json_export.py
10
+ examples/labels.py
11
+ examples/layer_control.py
12
+ examples/lines.py
13
+ examples/reactive.py
14
+ examples/simple.py
15
+ examples/tiles.py
16
+ geovizpy/__init__.py
17
+ geovizpy/controls.py
18
+ geovizpy/effects.py
19
+ geovizpy/geoviz.py
20
+ geovizpy/insets.py
21
+ geovizpy/legends.py
22
+ geovizpy/marks.py
23
+ geovizpy/plots.py
24
+ geovizpy/renderer.py
25
+ geovizpy.egg-info/PKG-INFO
26
+ geovizpy.egg-info/SOURCES.txt
27
+ geovizpy.egg-info/dependency_links.txt
28
+ geovizpy.egg-info/requires.txt
29
+ geovizpy.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ docs
2
+ examples
3
+ geoviz_src
4
+ geovizpy
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "geovizpy"
7
+ version = "0.1.8"
8
+ description = "A Python wrapper for the geoviz JavaScript library"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [{name = "fbxyz"}]
12
+ requires-python = ">=3.6"
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ export = ["playwright"]
21
+
22
+ [project.urls]
23
+ Source = "https://codeberg.org/fbxyz/geovizpy"
24
+
25
+ [tool.setuptools.packages.find]
26
+ where = ["."]
@@ -9,7 +9,7 @@ long_description = (this_directory / "README.md").read_text()
9
9
 
10
10
  setup(
11
11
  name="geovizpy",
12
- version="0.1.6",
12
+ version="0.1.8",
13
13
  description="A Python wrapper for the geoviz JavaScript library",
14
14
  long_description=long_description,
15
15
  long_description_content_type='text/markdown',
@@ -1,15 +0,0 @@
1
- README.md
2
- setup.py
3
- geovizpy/__init__.py
4
- geovizpy/controls.py
5
- geovizpy/effects.py
6
- geovizpy/geoviz.py
7
- geovizpy/legends.py
8
- geovizpy/marks.py
9
- geovizpy/plots.py
10
- geovizpy/renderer.py
11
- geovizpy.egg-info/PKG-INFO
12
- geovizpy.egg-info/SOURCES.txt
13
- geovizpy.egg-info/dependency_links.txt
14
- geovizpy.egg-info/requires.txt
15
- geovizpy.egg-info/top_level.txt
@@ -1 +0,0 @@
1
- geovizpy
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes