nadata 0.1.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.
Files changed (86) hide show
  1. nadata/__init__.py +15 -0
  2. nadata/api.py +894 -0
  3. nadata/cli.py +445 -0
  4. nadata/console.py +486 -0
  5. nadata/core/__init__.py +0 -0
  6. nadata/core/database/__init__.py +38 -0
  7. nadata/core/database/builtins.py +87 -0
  8. nadata/core/database/database.py +958 -0
  9. nadata/core/database/dumper.py +285 -0
  10. nadata/core/database/entity_id.py +142 -0
  11. nadata/core/database/instance.py +706 -0
  12. nadata/core/database/network_index.py +1142 -0
  13. nadata/core/database/page_manager.py +175 -0
  14. nadata/core/database/persistence/__init__.py +28 -0
  15. nadata/core/database/persistence/binary_cells.py +1141 -0
  16. nadata/core/database/persistence/compaction.py +97 -0
  17. nadata/core/database/persistence/dumper.py +798 -0
  18. nadata/core/database/persistence/lazy_instances.py +1537 -0
  19. nadata/core/database/persistence/loader.py +424 -0
  20. nadata/core/database/persistence/record_store.py +228 -0
  21. nadata/core/database/persistence/serialization.py +605 -0
  22. nadata/core/database/query_engine.py +222 -0
  23. nadata/core/database/queryset.py +508 -0
  24. nadata/core/database/reconciliation.py +287 -0
  25. nadata/core/database/slotted_page.py +190 -0
  26. nadata/core/database/spatial_index.py +1080 -0
  27. nadata/core/expressions/__init__.py +44 -0
  28. nadata/core/expressions/autograd.py +2909 -0
  29. nadata/core/expressions/builtins.py +4673 -0
  30. nadata/core/expressions/expression.py +2658 -0
  31. nadata/core/expressions/io_builtins.py +972 -0
  32. nadata/core/expressions/scope.py +336 -0
  33. nadata/core/expressions/statement.py +953 -0
  34. nadata/core/struct/__init__.py +14 -0
  35. nadata/core/struct/struct.py +218 -0
  36. nadata/core/struct/struct_attribute.py +100 -0
  37. nadata/core/struct/struct_method.py +324 -0
  38. nadata/core/types/__init__.py +33 -0
  39. nadata/core/types/bits/__init__.py +0 -0
  40. nadata/core/types/bits/bits.py +686 -0
  41. nadata/core/types/home_types/__init__.py +19 -0
  42. nadata/core/types/home_types/color.py +102 -0
  43. nadata/core/types/home_types/coordinate.py +1008 -0
  44. nadata/core/types/home_types/datetime.py +362 -0
  45. nadata/core/types/home_types/geogrid.py +92 -0
  46. nadata/core/types/home_types/geometry.py +1223 -0
  47. nadata/core/types/home_types/graph.py +0 -0
  48. nadata/core/types/home_types/planet.py +359 -0
  49. nadata/core/types/home_types/sea_level.py +98 -0
  50. nadata/core/types/home_types/vec.py +0 -0
  51. nadata/core/types/types.py +1345 -0
  52. nadata/core/values/__init__.py +14 -0
  53. nadata/core/values/values.py +1568 -0
  54. nadata/parser/__init__.py +0 -0
  55. nadata/parser/expressions/__init__.py +6 -0
  56. nadata/parser/expressions/dependencies.py +318 -0
  57. nadata/parser/expressions/expression_parser.py +993 -0
  58. nadata/parser/expressions/splitter.py +98 -0
  59. nadata/parser/expressions/statement_parser.py +1425 -0
  60. nadata/parser/expressions/tokenizer.py +402 -0
  61. nadata/parser/expressions/type_parser.py +187 -0
  62. nadata/parser/nadata_loader/__init__.py +8 -0
  63. nadata/parser/nadata_loader/loader.py +951 -0
  64. nadata/parser/nastruct_parser/__init__.py +0 -0
  65. nadata/parser/nastruct_parser/declaration.py +105 -0
  66. nadata/parser/nastruct_parser/imports.py +164 -0
  67. nadata/parser/nastruct_parser/nastruct_parser.py +209 -0
  68. nadata/parser/nastruct_parser/parse_attributes.py +149 -0
  69. nadata/parser/nastruct_parser/parse_callables.py +436 -0
  70. nadata/parser/nastruct_parser/parse_expression.py +59 -0
  71. nadata/parser/nastruct_parser/parse_structs.py +206 -0
  72. nadata/parser/nastruct_parser/parser_state.py +84 -0
  73. nadata/parser/nastruct_parser/resolver.py +1412 -0
  74. nadata/parser/nastruct_parser/struct_body_state.py +50 -0
  75. nadata/parser/nastruct_parser/tmp_structs.py +156 -0
  76. nadata/parser/utils/__init__.py +20 -0
  77. nadata/parser/utils/brackets.py +252 -0
  78. nadata/parser/utils/comments_remove.py +149 -0
  79. nadata/parser/utils/identifiers.py +121 -0
  80. nadata/session.py +868 -0
  81. nadata-0.1.0.dist-info/METADATA +191 -0
  82. nadata-0.1.0.dist-info/RECORD +86 -0
  83. nadata-0.1.0.dist-info/WHEEL +5 -0
  84. nadata-0.1.0.dist-info/entry_points.txt +2 -0
  85. nadata-0.1.0.dist-info/licenses/LICENSE +21 -0
  86. nadata-0.1.0.dist-info/top_level.txt +1 -0
File without changes
@@ -0,0 +1,359 @@
1
+ """
2
+ Planet representations for Nadata.
3
+
4
+ Provides the Planet class describing the shape of the world coordinates
5
+ live on: a perfect sphere (single radius) or an oblate/prolate spheroid
6
+ (equatorial and polar radii). Also hosts the session-level current
7
+ planet, resolved from project settings (planet.kind, planet.radius,
8
+ planet.equatorial_radius, planet.polar_radius) and consulted by
9
+ geodesy builtins when no explicit planet override is passed.
10
+ """
11
+
12
+ # Import Modules
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, Optional
16
+ import math
17
+
18
+
19
+
20
+ # Reference radii (meters)
21
+ EARTH_EQUATORIAL_RADIUS: float = 6378137.0
22
+ EARTH_POLAR_RADIUS: float = 6356752.314245
23
+ EARTH_MEAN_RADIUS: float = 6371008.8
24
+
25
+ # Kind names for planet settings
26
+ KIND_SPHERE: str = "sphere"
27
+ KIND_ELLIPSOID: str = "ellipsoid"
28
+
29
+ # Session-level current planet (seeded by the loader from project
30
+ # settings; single-process assumption, see the map plan open points)
31
+ _current_planet: Optional["Planet"] = None
32
+
33
+
34
+
35
+ def _require_positive_length(name: str, value: Any) -> float:
36
+ """
37
+ Validates a length argument.
38
+
39
+ Args:
40
+ name (str): Argument name for error messages.
41
+ value (Any): Candidate length.
42
+
43
+ Returns:
44
+ float: The validated length.
45
+
46
+ Raises:
47
+ TypeError: If the value is not a real number.
48
+ ValueError: If the value is not strictly positive.
49
+ """
50
+
51
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
52
+ raise TypeError(f"Planet {name} must be a number, got '{type(value).__name__}'")
53
+
54
+ if not math.isfinite(float(value)) or float(value) <= 0.0:
55
+ raise ValueError(f"Planet {name} must be a finite positive length, got {value}")
56
+
57
+ return float(value)
58
+
59
+
60
+
61
+ class Planet:
62
+ """
63
+ Describes the shape of a world: sphere or spheroid.
64
+
65
+ Attributes:
66
+ equatorial_radius (float): Equatorial radius in meters.
67
+ polar_radius (Optional[float]): Polar radius in meters, or None
68
+ for a perfect sphere.
69
+ """
70
+
71
+ def __init__(
72
+ self,
73
+ equatorial_radius: float = EARTH_MEAN_RADIUS,
74
+ polar_radius: Optional[float] = None,
75
+ ) -> None:
76
+ """
77
+ Initializes the Planet instance.
78
+
79
+ Args:
80
+ equatorial_radius (float, optional): Equatorial radius in
81
+ meters. Defaults to the Earth mean radius.
82
+ polar_radius (Optional[float], optional): Polar radius in
83
+ meters. None yields a perfect sphere. Defaults to None.
84
+
85
+ Raises:
86
+ TypeError: If a radius is not a real number.
87
+ ValueError: If a radius is not a finite positive length.
88
+ """
89
+
90
+ self.equatorial_radius: float = _require_positive_length(
91
+ name="equatorial_radius", value=equatorial_radius
92
+ )
93
+
94
+ self.polar_radius: Optional[float] = (
95
+ None
96
+ if polar_radius is None
97
+ else _require_positive_length(name="polar_radius", value=polar_radius)
98
+ )
99
+
100
+ @classmethod
101
+ def sphere(cls, radius: float) -> "Planet":
102
+ """
103
+ Creates a perfect sphere planet.
104
+
105
+ Args:
106
+ radius (float): Sphere radius in meters.
107
+
108
+ Returns:
109
+ Planet: The sphere planet.
110
+ """
111
+
112
+ return cls(equatorial_radius=radius, polar_radius=None)
113
+
114
+ @property
115
+ def is_sphere(self) -> bool:
116
+ """
117
+ Returns whether the planet is a perfect sphere.
118
+
119
+ Returns:
120
+ bool: True when no distinct polar radius is set.
121
+ """
122
+
123
+ return self.polar_radius is None
124
+
125
+ @property
126
+ def polar_radius_value(self) -> float:
127
+ """
128
+ Returns the effective polar radius (equals the equatorial
129
+ radius for spheres).
130
+
131
+ Returns:
132
+ float: Polar radius in meters.
133
+ """
134
+
135
+ return (
136
+ self.equatorial_radius
137
+ if self.polar_radius is None
138
+ else self.polar_radius
139
+ )
140
+
141
+ @property
142
+ def flattening(self) -> float:
143
+ """
144
+ Returns the flattening f = (a - b) / a.
145
+
146
+ Returns:
147
+ float: Flattening (0.0 for spheres).
148
+ """
149
+
150
+ return (self.equatorial_radius - self.polar_radius_value) / self.equatorial_radius
151
+
152
+ @property
153
+ def eccentricity_squared(self) -> float:
154
+ """
155
+ Returns the first eccentricity squared e^2 = 2f - f^2.
156
+
157
+ Returns:
158
+ float: Eccentricity squared (0.0 for spheres).
159
+ """
160
+
161
+ flattening: float = self.flattening
162
+
163
+ return 2.0 * flattening - flattening * flattening
164
+
165
+ @property
166
+ def quadratic_mean_radius(self) -> float:
167
+ """
168
+ Returns the quadratic mean radius sqrt((a^2 + b^2) / 2), used as
169
+ the sphere fallback for near-antipodal spheroid distances.
170
+
171
+ Returns:
172
+ float: Quadratic mean radius in meters.
173
+ """
174
+
175
+ squares: float = (
176
+ self.equatorial_radius ** 2 + self.polar_radius_value ** 2
177
+ )
178
+
179
+ return math.sqrt(squares / 2.0)
180
+
181
+ @property
182
+ def authalic_radius(self) -> float:
183
+ """
184
+ Returns the authalic (equal-area) radius: the radius of the
185
+ sphere with the same surface area as the spheroid. Used to
186
+ scale spherical polygon areas to spheroids.
187
+
188
+ Returns:
189
+ float: Authalic radius in meters.
190
+ """
191
+
192
+ if self.is_sphere:
193
+ return self.equatorial_radius
194
+
195
+ e: float = math.sqrt(self.eccentricity_squared)
196
+
197
+ if e == 0.0:
198
+ return self.equatorial_radius
199
+
200
+ surface_area: float = (
201
+ 2.0
202
+ * math.pi
203
+ * self.equatorial_radius ** 2
204
+ * (1.0 + (1.0 - e * e) / e * math.atanh(e))
205
+ )
206
+
207
+ return math.sqrt(surface_area / (4.0 * math.pi))
208
+
209
+ def surface_radius(self, lat_deg: float) -> float:
210
+ """
211
+ Returns the distance from the planet center to the surface at a
212
+ geodetic latitude.
213
+
214
+ Args:
215
+ lat_deg (float): Geodetic latitude in degrees.
216
+
217
+ Returns:
218
+ float: Surface radius in meters.
219
+ """
220
+
221
+ if self.is_sphere:
222
+ return self.equatorial_radius
223
+
224
+ lat_rad: float = math.radians(lat_deg)
225
+ sin_lat: float = math.sin(lat_rad)
226
+
227
+ prime_vertical: float = self.equatorial_radius / math.sqrt(
228
+ 1.0 - self.eccentricity_squared * sin_lat * sin_lat
229
+ )
230
+
231
+ x: float = prime_vertical * math.cos(lat_rad)
232
+ z: float = prime_vertical * (1.0 - self.eccentricity_squared) * sin_lat
233
+
234
+ return math.hypot(x, z)
235
+
236
+ def geocentric_latitude(self, lat_deg: float) -> float:
237
+ """
238
+ Converts a geodetic latitude to the geocentric latitude of the
239
+ surface point below it.
240
+
241
+ Args:
242
+ lat_deg (float): Geodetic latitude in degrees.
243
+
244
+ Returns:
245
+ float: Geocentric latitude in degrees.
246
+ """
247
+
248
+ if self.is_sphere:
249
+ return lat_deg
250
+
251
+ ratio: float = (self.polar_radius_value ** 2) / (self.equatorial_radius ** 2)
252
+
253
+ return math.degrees(math.atan(ratio * math.tan(math.radians(lat_deg))))
254
+
255
+ def __eq__(self, other: Any) -> bool:
256
+ """
257
+ Checks equality with another Planet.
258
+
259
+ Args:
260
+ other (Any): Object to compare with.
261
+
262
+ Returns:
263
+ bool: True when both radii match.
264
+ """
265
+
266
+ if not isinstance(other, Planet):
267
+ return False
268
+
269
+ return (
270
+ self.equatorial_radius == other.equatorial_radius
271
+ and self.polar_radius == other.polar_radius
272
+ )
273
+
274
+ def __repr__(self) -> str:
275
+ """
276
+ Returns string representation of the Planet.
277
+
278
+ Returns:
279
+ str: Planet constructor string.
280
+ """
281
+
282
+ if self.is_sphere:
283
+ return f"Planet({self.equatorial_radius})"
284
+
285
+ return f"Planet({self.equatorial_radius}, {self.polar_radius})"
286
+
287
+
288
+ # Default planet: a perfect sphere of the Earth mean radius
289
+ DEFAULT_PLANET: Planet = Planet(equatorial_radius=EARTH_MEAN_RADIUS)
290
+
291
+
292
+ def get_current_planet() -> Planet:
293
+ """
294
+ Returns the session current planet, falling back to the default
295
+ Earth sphere when no project has configured one.
296
+
297
+ Returns:
298
+ Planet: The current planet.
299
+ """
300
+
301
+ return _current_planet if _current_planet is not None else DEFAULT_PLANET
302
+
303
+
304
+ def set_current_planet(planet: Planet) -> None:
305
+ """
306
+ Replaces the session current planet.
307
+
308
+ Args:
309
+ planet (Planet): The new current planet.
310
+ """
311
+
312
+ global _current_planet
313
+
314
+ if not isinstance(planet, Planet):
315
+ raise TypeError(f"Expected Planet, got '{type(planet).__name__}'")
316
+
317
+ _current_planet = planet
318
+
319
+
320
+ def planet_from_settings(settings: dict[str, Any]) -> Planet:
321
+ """
322
+ Builds a Planet from project settings.
323
+
324
+ Recognized keys:
325
+ - "planet.kind": "sphere" (default) or "ellipsoid".
326
+ - "planet.radius": sphere radius (sphere kind).
327
+ - "planet.equatorial_radius" / "planet.polar_radius": spheroid
328
+ radii (ellipsoid kind; defaults to the WGS84 values).
329
+
330
+ Args:
331
+ settings (dict[str, Any]): The project settings mapping.
332
+
333
+ Returns:
334
+ Planet: The configured planet.
335
+
336
+ Raises:
337
+ ValueError: If the kind is unknown.
338
+ """
339
+
340
+ kind: str = settings.get("planet.kind", KIND_SPHERE)
341
+
342
+ if kind == KIND_ELLIPSOID:
343
+ return Planet(
344
+ equatorial_radius=settings.get(
345
+ "planet.equatorial_radius", EARTH_EQUATORIAL_RADIUS
346
+ ),
347
+ polar_radius=settings.get(
348
+ "planet.polar_radius", EARTH_POLAR_RADIUS
349
+ ),
350
+ )
351
+
352
+ if kind == KIND_SPHERE:
353
+ return Planet(
354
+ equatorial_radius=settings.get("planet.radius", EARTH_MEAN_RADIUS)
355
+ )
356
+
357
+ raise ValueError(
358
+ f"Unknown planet kind '{kind}' (expected 'sphere' or 'ellipsoid')"
359
+ )
@@ -0,0 +1,98 @@
1
+ """
2
+ Session sea-level surface registry.
3
+
4
+ A registered sea-level layer overrides the plain surface radius used
5
+ by altitude() (and any consumer of sea level): the layer is a sparse
6
+ GeoGrid of sea-surface radii; where it has no data the geometric
7
+ surface radius applies.
8
+
9
+ The registry stores duck-typed adapters only, so it can be imported
10
+ from the geodesy core without circular dependencies. Like the session
11
+ planet, the registry is process-global state.
12
+ """
13
+
14
+ # Import Modules
15
+ from typing import Any, Optional
16
+
17
+ from nadata.core.types.home_types.geogrid import bilinear_sample
18
+
19
+
20
+
21
+ class GeoSeaLevelAdapter:
22
+ """
23
+ Adapter exposing a GeoGrid value as a sea-level surface.
24
+
25
+ Attributes:
26
+ cells (dict[tuple[int, int], Any]): Occupied cells keyed by
27
+ (column, row).
28
+ n_x (int): Longitude column count.
29
+ n_y (int): Latitude row count.
30
+ cell_deg (float): Cell edge in degrees.
31
+ """
32
+
33
+ def __init__(self, grid: Any) -> None:
34
+ """
35
+ Initializes the GeoSeaLevelAdapter instance.
36
+
37
+ Args:
38
+ grid (Any): A ValueGeoGrid-like object with cells and a
39
+ TypeGeoGrid-like value_type.
40
+ """
41
+
42
+ self.cells: dict[tuple[int, int], Any] = grid.cells
43
+ self.n_x: int = grid.value_type.n_x
44
+ self.n_y: int = grid.value_type.n_y
45
+ self.cell_deg: float = grid.value_type.cell_deg
46
+
47
+ def radius_at(self, lat_deg: float, lon_deg: float) -> Optional[float]:
48
+ """
49
+ Samples the sea-surface radius at a coordinate.
50
+
51
+ Args:
52
+ lat_deg (float): Latitude in degrees.
53
+ lon_deg (float): Longitude in degrees.
54
+
55
+ Returns:
56
+ Optional[float]: The interpolated sea-surface radius in
57
+ meters, or None when no surrounding cell has data.
58
+ """
59
+
60
+ return bilinear_sample(
61
+ cells=self.cells,
62
+ n_x=self.n_x,
63
+ n_y=self.n_y,
64
+ cell_deg=self.cell_deg,
65
+ lat_deg=lat_deg,
66
+ lon_deg=lon_deg,
67
+ )
68
+
69
+
70
+
71
+ _SEA_LEVEL_SURFACE: Optional[Any] = None
72
+
73
+
74
+
75
+ def get_sea_level_surface() -> Optional[Any]:
76
+ """
77
+ Returns the registered sea-level surface, if any.
78
+
79
+ Returns:
80
+ Optional[Any]: The surface adapter, or None.
81
+ """
82
+
83
+ return _SEA_LEVEL_SURFACE
84
+
85
+
86
+
87
+ def set_sea_level_surface(surface: Optional[Any]) -> None:
88
+ """
89
+ Replaces the session sea-level surface.
90
+
91
+ Args:
92
+ surface (Optional[Any]): The surface adapter, or None to
93
+ unregister.
94
+ """
95
+
96
+ global _SEA_LEVEL_SURFACE
97
+
98
+ _SEA_LEVEL_SURFACE = surface
File without changes