cesiumjs-anywidget 0.7.0__py3-none-any.whl → 0.8.0__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.
@@ -9,7 +9,6 @@ from .geoid import (
9
9
  set_geoid_data_url,
10
10
  )
11
11
  from .logger import get_logger, set_log_level
12
- from .exif_utils import extract_all_metadata, extract_gps_data, extract_datetime
13
12
 
14
13
  __version__ = "0.6.0"
15
14
  __all__ = [
@@ -21,7 +20,4 @@ __all__ = [
21
20
  "set_geoid_data_url",
22
21
  "get_logger",
23
22
  "set_log_level",
24
- "extract_all_metadata",
25
- "extract_gps_data",
26
- "extract_datetime",
27
23
  ]
@@ -7,7 +7,6 @@ The EGM96 15-minute grid data is automatically downloaded from the internet on
7
7
  first use and cached locally. You can customize the data source URL if needed.
8
8
  """
9
9
 
10
- import os
11
10
  import tarfile
12
11
  import urllib.request
13
12
  import zipfile
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cesiumjs-anywidget
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: A Jupyter widget for CesiumJS 3D globe visualization using anywidget
5
5
  Project-URL: Homepage, https://github.com/Alex-PLACET/cesiumjs_anywidget
6
6
  Project-URL: Repository, https://github.com/Alex-PLACET/cesiumjs_anywidget
@@ -216,25 +216,10 @@ Classifier: Programming Language :: Python :: 3
216
216
  Classifier: Programming Language :: Python :: 3.12
217
217
  Requires-Python: >=3.12
218
218
  Requires-Dist: anywidget>=0.9.0
219
- Requires-Dist: exifread>=3.5.1
220
- Requires-Dist: ipycanvas>=0.14.3
221
- Requires-Dist: jupyterlab>=4.3.8
222
- Requires-Dist: matplotlib>=3.7.5
223
- Requires-Dist: numpy>=1.24.4
224
- Requires-Dist: opencv-python>=4.11.0.86
225
- Requires-Dist: pillow>=10.4.0
219
+ Requires-Dist: numpy>=2.4.0
226
220
  Requires-Dist: pygeodesy>=25.11.5
227
- Requires-Dist: scipy>=1.10.1
221
+ Requires-Dist: scipy>=1.17.0
228
222
  Requires-Dist: traitlets>=5.0.0
229
- Provides-Extra: dev
230
- Requires-Dist: huggingface-hub>=1.3.4; extra == 'dev'
231
- Requires-Dist: jupyterlab>=4.0.0; extra == 'dev'
232
- Requires-Dist: notebook>=7.0.0; extra == 'dev'
233
- Requires-Dist: piexif>=1.1.3; extra == 'dev'
234
- Requires-Dist: pyarrow>=23.0.0; extra == 'dev'
235
- Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
236
- Requires-Dist: pytest>=7.0.0; extra == 'dev'
237
- Requires-Dist: sidecar>=0.8.0; extra == 'dev'
238
223
  Description-Content-Type: text/markdown
239
224
 
240
225
  # CesiumJS Anywidget
@@ -0,0 +1,10 @@
1
+ cesiumjs_anywidget/__init__.py,sha256=2HDc9vodXRcFenpEYK9lZrDpWrrf3JddbqSKQYJ60jE,508
2
+ cesiumjs_anywidget/geoid.py,sha256=UJQM2p5OPtLcmbE9FCEYSiOg_nBSaSQ3JyvYNkIYHAw,9475
3
+ cesiumjs_anywidget/logger.py,sha256=W8yxB8ni1modVH7VdSiT0IbIZFGTr1Y-FPQDfpHA5ao,2037
4
+ cesiumjs_anywidget/styles.css,sha256=1N2nya5nsd5Lhq9FuEoMJ1_CVXj9XghdMmX-KC5xwQI,1278
5
+ cesiumjs_anywidget/widget.py,sha256=8L6CFM0eynUcpI8l8lUvS9d0xM3YEHImziMzdkhqIZE,59471
6
+ cesiumjs_anywidget/index.js,sha256=dGG7AtlBXbx8SLkrjpSjRfolRx2Fo-_N4rgDHBR-F-g,113957
7
+ cesiumjs_anywidget-0.8.0.dist-info/METADATA,sha256=IzTaPprNida4dLL38TZyifsybrDFj_KJoUYzanca7as,25606
8
+ cesiumjs_anywidget-0.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
9
+ cesiumjs_anywidget-0.8.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
10
+ cesiumjs_anywidget-0.8.0.dist-info/RECORD,,
@@ -1,281 +0,0 @@
1
- """EXIF data extraction utilities for photo geolocation."""
2
-
3
- import exifread
4
- from datetime import datetime
5
- from typing import Optional, Dict, Any, Tuple
6
- from pathlib import Path
7
- from .logger import get_logger
8
-
9
- logger = get_logger(__name__)
10
-
11
-
12
- def _convert_to_degrees(value) -> float:
13
- """Convert GPS coordinates to degrees.
14
-
15
- Parameters
16
- ----------
17
- value : exifread.utils.Ratio list
18
- GPS coordinate in degrees, minutes, seconds format
19
-
20
- Returns
21
- -------
22
- float
23
- Coordinate in decimal degrees
24
- """
25
- d = float(value.values[0].num) / float(value.values[0].den)
26
- m = float(value.values[1].num) / float(value.values[1].den)
27
- s = float(value.values[2].num) / float(value.values[2].den)
28
-
29
- # Normalize malformed DMS values from some writers
30
- # Some encoders store seconds as total arc-seconds * 60 (e.g., 1425 instead of 23.75)
31
- if s >= 60 and m < 60:
32
- s = s / 60.0
33
-
34
- if s >= 60:
35
- carry = int(s // 60)
36
- s = s - (carry * 60)
37
- m += carry
38
-
39
- if m >= 60:
40
- carry = int(m // 60)
41
- m = m - (carry * 60)
42
- d += carry
43
-
44
- return d + (m / 60.0) + (s / 3600.0)
45
-
46
-
47
- def extract_gps_data(image_path: str) -> Optional[Dict[str, float]]:
48
- """Extract GPS coordinates from image EXIF data.
49
-
50
- Parameters
51
- ----------
52
- image_path : str
53
- Path to the image file
54
-
55
- Returns
56
- -------
57
- dict or None
58
- Dictionary containing 'latitude', 'longitude', and optionally 'altitude'
59
- Returns None if GPS data is not available
60
-
61
- Examples
62
- --------
63
- >>> gps_data = extract_gps_data('photo.jpg')
64
- >>> if gps_data:
65
- ... print(f"Location: {gps_data['latitude']}, {gps_data['longitude']}")
66
- """
67
- try:
68
- with open(image_path, 'rb') as f:
69
- tags = exifread.process_file(f, details=False)
70
-
71
- # Check if GPS data exists
72
- if 'GPS GPSLatitude' not in tags or 'GPS GPSLongitude' not in tags:
73
- logger.warning(f"No GPS data found in {image_path}")
74
- return None
75
-
76
- # Extract latitude
77
- lat = _convert_to_degrees(tags['GPS GPSLatitude'])
78
- if tags.get('GPS GPSLatitudeRef', 'N').values[0] == 'S':
79
- lat = -lat
80
-
81
- # Extract longitude
82
- lon = _convert_to_degrees(tags['GPS GPSLongitude'])
83
- if tags.get('GPS GPSLongitudeRef', 'E').values[0] == 'W':
84
- lon = -lon
85
-
86
- result = {
87
- 'latitude': lat,
88
- 'longitude': lon,
89
- }
90
-
91
- # Extract altitude if available
92
- if 'GPS GPSAltitude' in tags:
93
- alt_tag = tags['GPS GPSAltitude']
94
- altitude = float(alt_tag.values[0].num) / float(alt_tag.values[0].den)
95
-
96
- # Check altitude reference (0 = above sea level, 1 = below sea level)
97
- if 'GPS GPSAltitudeRef' in tags:
98
- alt_ref = tags['GPS GPSAltitudeRef'].values[0]
99
- if alt_ref == 1:
100
- altitude = -altitude
101
-
102
- result['altitude'] = altitude
103
-
104
- logger.info(f"Extracted GPS data from {image_path}: {result}")
105
- return result
106
-
107
- except Exception as e:
108
- logger.error(f"Error extracting GPS data from {image_path}: {e}")
109
- return None
110
-
111
-
112
- def extract_datetime(image_path: str) -> Optional[datetime]:
113
- """Extract capture datetime from image EXIF data.
114
-
115
- Parameters
116
- ----------
117
- image_path : str
118
- Path to the image file
119
-
120
- Returns
121
- -------
122
- datetime or None
123
- Image capture datetime, or None if not available
124
-
125
- Examples
126
- --------
127
- >>> dt = extract_datetime('photo.jpg')
128
- >>> if dt:
129
- ... print(f"Captured on: {dt.strftime('%Y-%m-%d %H:%M:%S')}")
130
- """
131
- try:
132
- with open(image_path, 'rb') as f:
133
- tags = exifread.process_file(f, details=False)
134
-
135
- # Try different datetime tags
136
- for tag_name in ['EXIF DateTimeOriginal', 'EXIF DateTime', 'Image DateTime']:
137
- if tag_name in tags:
138
- dt_str = str(tags[tag_name])
139
- # Parse datetime (format: "YYYY:MM:DD HH:MM:SS")
140
- dt = datetime.strptime(dt_str, '%Y:%m:%d %H:%M:%S')
141
- logger.info(f"Extracted datetime from {image_path}: {dt}")
142
- return dt
143
-
144
- logger.warning(f"No datetime found in {image_path}")
145
- return None
146
-
147
- except Exception as e:
148
- logger.error(f"Error extracting datetime from {image_path}: {e}")
149
- return None
150
-
151
-
152
- def extract_camera_info(image_path: str) -> Dict[str, Any]:
153
- """Extract camera information from image EXIF data.
154
-
155
- Parameters
156
- ----------
157
- image_path : str
158
- Path to the image file
159
-
160
- Returns
161
- -------
162
- dict
163
- Dictionary containing camera make, model, focal length, sensor size, etc.
164
-
165
- Examples
166
- --------
167
- >>> info = extract_camera_info('photo.jpg')
168
- >>> print(f"Camera: {info.get('make')} {info.get('model')}")
169
- """
170
- result = {}
171
-
172
- try:
173
- with open(image_path, 'rb') as f:
174
- tags = exifread.process_file(f, details=False)
175
-
176
- # Camera make and model
177
- if 'Image Make' in tags:
178
- result['make'] = str(tags['Image Make'])
179
- if 'Image Model' in tags:
180
- result['model'] = str(tags['Image Model'])
181
-
182
- # Focal length
183
- if 'EXIF FocalLength' in tags:
184
- focal_tag = tags['EXIF FocalLength']
185
- focal_length = float(focal_tag.values[0].num) / float(focal_tag.values[0].den)
186
- result['focal_length_mm'] = focal_length
187
-
188
- # Focal length in 35mm equivalent
189
- if 'EXIF FocalLengthIn35mmFilm' in tags:
190
- result['focal_length_35mm'] = int(tags['EXIF FocalLengthIn35mmFilm'].values[0])
191
-
192
- # Image dimensions
193
- if 'EXIF ExifImageWidth' in tags:
194
- result['image_width'] = int(tags['EXIF ExifImageWidth'].values[0])
195
- if 'EXIF ExifImageLength' in tags:
196
- result['image_height'] = int(tags['EXIF ExifImageLength'].values[0])
197
-
198
- # Orientation
199
- if 'Image Orientation' in tags:
200
- result['orientation'] = str(tags['Image Orientation'])
201
-
202
- logger.info(f"Extracted camera info from {image_path}: {result}")
203
- return result
204
-
205
- except Exception as e:
206
- logger.error(f"Error extracting camera info from {image_path}: {e}")
207
- return result
208
-
209
-
210
- def get_image_dimensions(image_path: str) -> Optional[Tuple[int, int]]:
211
- """Get image dimensions (width, height) from file.
212
-
213
- Parameters
214
- ----------
215
- image_path : str
216
- Path to the image file
217
-
218
- Returns
219
- -------
220
- tuple or None
221
- (width, height) in pixels, or None if unable to read
222
- """
223
- try:
224
- from PIL import Image
225
- with Image.open(image_path) as img:
226
- return img.size
227
- except Exception as e:
228
- logger.error(f"Error reading image dimensions from {image_path}: {e}")
229
- return None
230
-
231
-
232
- def extract_all_metadata(image_path: str) -> Dict[str, Any]:
233
- """Extract all relevant metadata from an image.
234
-
235
- Combines GPS, datetime, camera info, and image dimensions into one dictionary.
236
-
237
- Parameters
238
- ----------
239
- image_path : str
240
- Path to the image file
241
-
242
- Returns
243
- -------
244
- dict
245
- Dictionary containing all extracted metadata
246
-
247
- Examples
248
- --------
249
- >>> metadata = extract_all_metadata('photo.jpg')
250
- >>> print(metadata)
251
- {'latitude': 48.8566, 'longitude': 2.3522, 'altitude': 100,
252
- 'datetime': datetime(2024, 1, 15, 14, 30, 0),
253
- 'make': 'Canon', 'model': 'EOS R5', ...}
254
- """
255
- metadata = {
256
- 'file_path': str(Path(image_path).absolute()),
257
- 'file_name': Path(image_path).name,
258
- }
259
-
260
- # GPS data
261
- gps_data = extract_gps_data(image_path)
262
- if gps_data:
263
- metadata.update(gps_data)
264
-
265
- # Datetime
266
- dt = extract_datetime(image_path)
267
- if dt:
268
- metadata['datetime'] = dt
269
- metadata['datetime_str'] = dt.isoformat()
270
-
271
- # Camera info
272
- camera_info = extract_camera_info(image_path)
273
- metadata.update(camera_info)
274
-
275
- # Image dimensions
276
- dims = get_image_dimensions(image_path)
277
- if dims:
278
- metadata['width'] = dims[0]
279
- metadata['height'] = dims[1]
280
-
281
- return metadata
@@ -1,11 +0,0 @@
1
- cesiumjs_anywidget/__init__.py,sha256=mdoSqnikLbSjLK8SYDpey40DHKzgsNUAQoIeLR2PMBQ,665
2
- cesiumjs_anywidget/exif_utils.py,sha256=Avk_JZAPk8EjGGluLK9tc-V8GE52rR0aDYgM113p2Z4,8439
3
- cesiumjs_anywidget/geoid.py,sha256=aS-GtzQ0f8XOm-FHbCpDuIQ5PN4QpMYYS4Wf8q1gb-I,9485
4
- cesiumjs_anywidget/logger.py,sha256=W8yxB8ni1modVH7VdSiT0IbIZFGTr1Y-FPQDfpHA5ao,2037
5
- cesiumjs_anywidget/styles.css,sha256=1N2nya5nsd5Lhq9FuEoMJ1_CVXj9XghdMmX-KC5xwQI,1278
6
- cesiumjs_anywidget/widget.py,sha256=8L6CFM0eynUcpI8l8lUvS9d0xM3YEHImziMzdkhqIZE,59471
7
- cesiumjs_anywidget/index.js,sha256=dGG7AtlBXbx8SLkrjpSjRfolRx2Fo-_N4rgDHBR-F-g,113957
8
- cesiumjs_anywidget-0.7.0.dist-info/METADATA,sha256=iN4D86KZ5Q6S_rbKmiAdgymyNItlzdULdAu2IXYPFSs,26209
9
- cesiumjs_anywidget-0.7.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
10
- cesiumjs_anywidget-0.7.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
11
- cesiumjs_anywidget-0.7.0.dist-info/RECORD,,