geovizpy 0.1.5__py3-none-any.whl → 0.1.6__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/renderer.py CHANGED
@@ -4,6 +4,9 @@ import json
4
4
  import tempfile
5
5
  import os
6
6
  import time
7
+ import html
8
+ import sys
9
+ import subprocess
7
10
 
8
11
  class RendererMixin:
9
12
  """Mixin class for rendering the map."""
@@ -33,14 +36,14 @@ class RendererMixin:
33
36
  """Return the configuration as a JSON string."""
34
37
  return json.dumps(self.get_config())
35
38
 
36
- def render_html(self, filename="map.html"):
37
- """Render the map to an HTML file."""
39
+ def _get_html_content(self):
40
+ """Generate the full HTML content string."""
38
41
  json_commands = self.to_json()
39
42
 
40
43
  layer_control_js = self._get_layer_control_js()
41
44
  export_control_js = self._get_export_control_js()
42
45
 
43
- html_content = f"""
46
+ return f"""
44
47
  <!DOCTYPE html>
45
48
  <html>
46
49
  <head>
@@ -49,7 +52,7 @@ class RendererMixin:
49
52
  <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
50
53
  <script src="https://cdn.jsdelivr.net/npm/geoviz@0.9.8"></script>
51
54
  <style>
52
- body {{ margin: 0; padding: 0; }}
55
+ body {{ margin: 0; padding: 0; overflow: hidden; }}
53
56
  button {{ background: #f8f9fa; border: 1px solid #ddd; border-radius: 3px; }}
54
57
  button:hover {{ background: #e2e6ea; }}
55
58
  </style>
@@ -111,10 +114,35 @@ class RendererMixin:
111
114
  </body>
112
115
  </html>
113
116
  """
117
+
118
+ def render_html(self, filename="map.html"):
119
+ """Render the map to an HTML file."""
120
+ html_content = self._get_html_content()
114
121
  with open(filename, "w") as f:
115
122
  f.write(html_content)
116
123
  print(f"Map saved to {filename}")
117
124
 
125
+ def show(self, width=800, height=600):
126
+ """
127
+ Display the map in a Jupyter notebook using an IFrame.
128
+
129
+ Args:
130
+ width (int/str): Width of the display area (default 800).
131
+ height (int/str): Height of the display area (default 600).
132
+ """
133
+ try:
134
+ from IPython.display import IFrame
135
+ import base64
136
+ except ImportError:
137
+ print("IPython is required to display the map. Please install it with 'pip install ipython'.")
138
+ return
139
+
140
+ html_content = self._get_html_content()
141
+ b64_content = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
142
+ data_uri = f"data:text/html;base64,{b64_content}"
143
+
144
+ return IFrame(src=data_uri, width=width, height=height)
145
+
118
146
  def save(self, filename="map.html"):
119
147
  """
120
148
  Save the map to a file.
@@ -134,39 +162,69 @@ class RendererMixin:
134
162
  print("Error: filename must end with .html, .png, or .svg")
135
163
 
136
164
  def _save_image(self, filename):
137
- """Internal method to save as PNG or SVG using Playwright."""
165
+ """Internal method to save as PNG or SVG using Playwright via a subprocess."""
166
+
167
+ # Check if playwright is installed
138
168
  try:
139
- from playwright.sync_api import sync_playwright
169
+ import playwright
140
170
  except ImportError:
141
171
  print("Error: Playwright is required for image export.")
142
172
  print("Please install it with: pip install geovizpy[export] && playwright install")
143
173
  return
144
174
 
175
+ # Create a temporary HTML file
145
176
  with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".html") as tmp_file:
146
177
  self.render_html(tmp_file.name)
147
178
  tmp_path = tmp_file.name
148
179
 
180
+ # Create a temporary Python script to run Playwright
181
+ # This isolates Playwright from the current asyncio loop (Jupyter)
182
+ script_content = f"""
183
+ import os
184
+ from playwright.sync_api import sync_playwright
185
+
186
+ def run():
187
+ try:
188
+ with sync_playwright() as p:
189
+ browser = p.chromium.launch()
190
+ page = browser.new_page(viewport={{"width": 1000, "height": 800}})
191
+ page.goto(f"file://{{os.path.abspath('{tmp_path}')}}")
192
+ page.wait_for_timeout(2000)
193
+
194
+ if "{filename}".endswith(".svg"):
195
+ svg_outer = page.locator("svg").first.evaluate("el => el.outerHTML")
196
+ with open("{filename}", "w") as f:
197
+ f.write(svg_outer)
198
+ else:
199
+ page.locator("svg").first.screenshot(path="{filename}")
200
+
201
+ browser.close()
202
+ print(f"Image saved to {filename}")
203
+ except Exception as e:
204
+ print(f"Error in subprocess: {{e}}")
205
+ exit(1)
206
+
207
+ if __name__ == "__main__":
208
+ run()
209
+ """
210
+
211
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".py") as tmp_script:
212
+ tmp_script.write(script_content)
213
+ tmp_script_path = tmp_script.name
214
+
149
215
  try:
150
- with sync_playwright() as p:
151
- browser = p.chromium.launch()
152
- page = browser.new_page(viewport={"width": 1000, "height": 800})
153
- page.goto(f"file://{os.path.abspath(tmp_path)}")
154
- page.wait_for_timeout(2000)
155
-
156
- if filename.endswith(".svg"):
157
- svg_outer = page.locator("svg").first.evaluate("el => el.outerHTML")
158
- with open(filename, "w") as f:
159
- f.write(svg_outer)
160
- else: # .png
161
- page.locator("svg").first.screenshot(path=filename)
162
-
163
- browser.close()
164
- print(f"Image saved to {filename}")
165
- except Exception as e:
166
- print(f"Error saving image: {e}")
216
+ # Run the script in a subprocess
217
+ result = subprocess.run([sys.executable, tmp_script_path], capture_output=True, text=True)
218
+ if result.returncode != 0:
219
+ print(f"Error saving image: {result.stderr}")
220
+ else:
221
+ print(result.stdout.strip())
167
222
  finally:
223
+ # Cleanup
168
224
  if os.path.exists(tmp_path):
169
225
  os.remove(tmp_path)
226
+ if os.path.exists(tmp_script_path):
227
+ os.remove(tmp_script_path)
170
228
 
171
229
  def _get_layer_control_js(self):
172
230
  if not self.layer_control_config:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geovizpy
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: A Python wrapper for the geoviz JavaScript library
5
5
  Author: fbxyz
6
6
  Project-URL: Source, https://codeberg.org/fbxyz/geovizpy
@@ -24,7 +24,9 @@ Dynamic: summary
24
24
 
25
25
  **geovizpy** is a Python wrapper for the `geoviz` JavaScript library, designed to bring the power of D3.js-based thematic mapping to Python. It allows you to create high-quality, interactive maps directly from Python scripts or Jupyter notebooks.
26
26
 
27
- This library is a wrapper around the excellent `geoviz` library. For detailed information on the underlying mapping logic, please refer to the [original geoviz documentation](https://github.com/neocarto/geoviz).
27
+ This library is a wrapper around the `geoviz` library. For detailed information on the underlying mapping logic, please refer to the [original geoviz documentation](https://github.com/neocarto/geoviz).
28
+
29
+ ![Choropleth Map Example](https://codeberg.org/fbxyz/geovizpy/raw/branch/main/docs/source/_static/choropleth.png)
28
30
 
29
31
  ## Features
30
32
 
@@ -53,7 +55,7 @@ pip install git+https://codeberg.org/fbxyz/geovizpy.git
53
55
 
54
56
  ### For Image Export
55
57
 
56
- To save maps as PNG or SVG files directly from Python, you need to install the optional `export` dependencies, which include `playwright`.
58
+ To save maps as PNG or SVG files directly from Python, you need to install the optional `export` dependencies:
57
59
 
58
60
  1. **Install the extra dependencies:**
59
61
 
@@ -113,5 +115,4 @@ viz.save("my_map.html") # Renders an interactive HTML file
113
115
 
114
116
  ## Documentation
115
117
 
116
- For more detailed information on all available methods and parameters, please see the [full documentation](https://your-username.github.io/geovizpy/).
117
-
118
+ For more detailed information on all available methods and parameters, please see the [full documentation](https://geovizpy.readthedocs.io/en/latest/).
@@ -5,8 +5,8 @@ geovizpy/geoviz.py,sha256=bG1A5AQIhsfIJ9RQ4RJteG9HRuu70k2xI9l_eydQTH0,1415
5
5
  geovizpy/legends.py,sha256=IPn_1drQ9MkmJlQ54XwBqAlTRSyq98sm8gzgE-ATRog,2218
6
6
  geovizpy/marks.py,sha256=xqwf8ELY05lnrrXGQljMe-mm315vUt_-qOmdFAsYhwA,5520
7
7
  geovizpy/plots.py,sha256=EVjPPBFyUFQpXH7sAuxCz_sctonJVUxoi8edbehKrpI,3089
8
- geovizpy/renderer.py,sha256=lrPZnAbvOdvtjA5DrcHkcID_CMwFcpreQwzDspf0ES4,14603
9
- geovizpy-0.1.5.dist-info/METADATA,sha256=SNuEBOpCeC8TT1o-bV5WkT3jB-E5wos0fGx9rd3EOcg,3386
10
- geovizpy-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- geovizpy-0.1.5.dist-info/top_level.txt,sha256=3D5AFdMd9bWEvGQUWDy6jccJamUwdKmchgCmxLHaAYs,9
12
- geovizpy-0.1.5.dist-info/RECORD,,
8
+ geovizpy/renderer.py,sha256=ql9eaRGo-274MEA6ZXcLY4QpidJdmHlt65IuHiwAbko,16543
9
+ geovizpy-0.1.6.dist-info/METADATA,sha256=l9WRhOd_vESt9qOK071kioRARK4OTvpCymOEtXRbHhE,3463
10
+ geovizpy-0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ geovizpy-0.1.6.dist-info/top_level.txt,sha256=3D5AFdMd9bWEvGQUWDy6jccJamUwdKmchgCmxLHaAYs,9
12
+ geovizpy-0.1.6.dist-info/RECORD,,