jupytergis-lab 0.1.1__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 (37) hide show
  1. jupytergis_lab/__init__.py +14 -0
  2. jupytergis_lab/_version.py +4 -0
  3. jupytergis_lab/notebook/__init__.py +1 -0
  4. jupytergis_lab/notebook/gis_document.py +616 -0
  5. jupytergis_lab/notebook/objects/__init__.py +14 -0
  6. jupytergis_lab/notebook/objects/_schema/__init__.py +3 -0
  7. jupytergis_lab/notebook/objects/_schema/geoTiffSource.py +24 -0
  8. jupytergis_lab/notebook/objects/_schema/geojsonsource.py +511 -0
  9. jupytergis_lab/notebook/objects/_schema/hillshadeLayer.py +19 -0
  10. jupytergis_lab/notebook/objects/_schema/imageLayer.py +19 -0
  11. jupytergis_lab/notebook/objects/_schema/imageSource.py +26 -0
  12. jupytergis_lab/notebook/objects/_schema/jgis.py +138 -0
  13. jupytergis_lab/notebook/objects/_schema/rasterDemSource.py +28 -0
  14. jupytergis_lab/notebook/objects/_schema/rasterlayer.py +19 -0
  15. jupytergis_lab/notebook/objects/_schema/rastersource.py +33 -0
  16. jupytergis_lab/notebook/objects/_schema/shapefileSource.py +31 -0
  17. jupytergis_lab/notebook/objects/_schema/vectorTileLayer.py +29 -0
  18. jupytergis_lab/notebook/objects/_schema/vectorlayer.py +29 -0
  19. jupytergis_lab/notebook/objects/_schema/vectortilesource.py +27 -0
  20. jupytergis_lab/notebook/objects/_schema/videoSource.py +30 -0
  21. jupytergis_lab/notebook/objects/_schema/webGlLayer.py +22 -0
  22. jupytergis_lab/notebook/tests/__init__.py +0 -0
  23. jupytergis_lab/notebook/tests/test_api.py +18 -0
  24. jupytergis_lab/notebook/utils.py +43 -0
  25. jupytergis_lab/notebook/y_connector.py +31 -0
  26. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/install.json +5 -0
  27. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/package.json +132 -0
  28. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/static/373.1c3d89f9ed56880711bd.js +1 -0
  29. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/static/432.28aaec36233a73d1589c.js +1 -0
  30. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/static/484.7cd11c502baf51c3285e.js +1 -0
  31. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/static/remoteEntry.15c6baaafaa709cc98c0.js +1 -0
  32. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/static/style.js +4 -0
  33. jupytergis_lab-0.1.1.data/data/share/jupyter/labextensions/@jupytergis/jupytergis-lab/static/third-party-licenses.json +40 -0
  34. jupytergis_lab-0.1.1.dist-info/METADATA +67 -0
  35. jupytergis_lab-0.1.1.dist-info/RECORD +37 -0
  36. jupytergis_lab-0.1.1.dist-info/WHEEL +4 -0
  37. jupytergis_lab-0.1.1.dist-info/licenses/LICENSE +29 -0
@@ -0,0 +1,14 @@
1
+ try:
2
+ from ._version import __version__
3
+ except ImportError:
4
+ # Fallback when using the package in dev mode without installing
5
+ # in editable mode with pip. It is highly recommended to install
6
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
7
+ import warnings
8
+ __version__ = "dev"
9
+
10
+ from .notebook import GISDocument # noqa
11
+
12
+
13
+ def _jupyter_labextension_paths():
14
+ return [{"src": "labextension", "dest": "@jupytergis/jupytergis-lab"}]
@@ -0,0 +1,4 @@
1
+ # This file is auto-generated by Hatchling. As such, do not:
2
+ # - modify
3
+ # - track in version control e.g. be sure to add to .gitignore
4
+ __version__ = VERSION = '0.1.1'
@@ -0,0 +1 @@
1
+ from .gis_document import GISDocument # noqa
@@ -0,0 +1,616 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Literal, Optional, Union
7
+
8
+ from pycrdt import Array, Doc, Map
9
+ from pydantic import BaseModel
10
+ from ypywidgets.comm import CommWidget
11
+
12
+ from uuid import uuid4
13
+
14
+ from .utils import normalize_path, get_source_layer_names
15
+
16
+ from .objects import (
17
+ LayerType,
18
+ SourceType,
19
+ IHillshadeLayer,
20
+ IImageLayer,
21
+ IRasterLayer,
22
+ IRasterSource,
23
+ IVectorTileSource,
24
+ IVectorLayer,
25
+ IVectorTileLayer,
26
+ IGeoJSONSource,
27
+ IImageSource,
28
+ IVideoSource,
29
+ IWebGlLayer
30
+ )
31
+
32
+ logger = logging.getLogger(__file__)
33
+
34
+
35
+ class GISDocument(CommWidget):
36
+ """
37
+ Create a new GISDocument object.
38
+
39
+ :param path: the path to the file that you would like to open.
40
+ If not provided, a new empty document will be created.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ path: Optional[str] = None,
46
+ latitude: Optional[float] = None,
47
+ longitude: Optional[float] = None,
48
+ zoom: Optional[float] = None,
49
+ extent: Optional[List[float]] = None,
50
+ bearing: Optional[float] = None,
51
+ pitch: Optional[float] = None,
52
+ projection: Optional[str] = None
53
+ ):
54
+ comm_metadata = GISDocument._path_to_comm(path)
55
+
56
+ ydoc = Doc()
57
+
58
+ super().__init__(
59
+ comm_metadata=dict(ymodel_name="@jupytergis:widget", **comm_metadata),
60
+ ydoc=ydoc,
61
+ )
62
+
63
+ self.ydoc["layers"] = self._layers = Map()
64
+ self.ydoc["sources"] = self._sources = Map()
65
+ self.ydoc["options"] = self._options = Map()
66
+ self.ydoc["layerTree"] = self._layerTree = Array()
67
+
68
+ if path is None:
69
+ if latitude is not None:
70
+ self._options["latitude"] = latitude
71
+ if longitude is not None:
72
+ self._options["longitude"] = longitude
73
+ if extent is not None:
74
+ self._options["extent"] = extent
75
+ if zoom is not None:
76
+ self._options["zoom"] = zoom
77
+ if bearing is not None:
78
+ self._options["bearing"] = bearing
79
+ if pitch is not None:
80
+ self._options["pitch"] = pitch
81
+ if projection is not None:
82
+ self._options['projection'] = projection
83
+
84
+ @property
85
+ def layers(self) -> Dict:
86
+ """
87
+ Get the layer list
88
+ """
89
+ return self._layers.to_py()
90
+
91
+ @property
92
+ def layer_tree(self) -> List[str | Dict]:
93
+ """
94
+ Get the layer tree
95
+ """
96
+ return self._layerTree.to_py()
97
+
98
+ def add_raster_layer(
99
+ self,
100
+ url: str,
101
+ name: str = "Raster Layer",
102
+ attribution: str = "",
103
+ opacity: float = 1,
104
+ ):
105
+ """
106
+ Add a Raster Layer to the document.
107
+
108
+ :param name: The name that will be used for the object in the document.
109
+ :param url: The tiles url.
110
+ :param attribution: The attribution.
111
+ :param opacity: The opacity, between 0 and 1.
112
+ """
113
+ source = {
114
+ "type": SourceType.RasterSource,
115
+ "name": f"{name} Source",
116
+ "parameters": {
117
+ "url": url,
118
+ "minZoom": 0,
119
+ "maxZoom": 24,
120
+ "attribution": attribution,
121
+ "htmlAttribution": attribution,
122
+ "provider": "",
123
+ "bounds": [],
124
+ "urlParameters": {},
125
+ },
126
+ }
127
+
128
+ source_id = self._add_source(OBJECT_FACTORY.create_source(source, self))
129
+
130
+ layer = {
131
+ "type": LayerType.RasterLayer,
132
+ "name": name,
133
+ "visible": True,
134
+ "parameters": {"source": source_id, "opacity": opacity},
135
+ }
136
+
137
+ return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))
138
+
139
+ def add_vectortile_layer(
140
+ self,
141
+ url: str,
142
+ name: str = "Vector Tile Layer",
143
+ source_layer: str | None = None,
144
+ attribution: str = "",
145
+ min_zoom: int = 0,
146
+ max_zoom: int = 24,
147
+ type: Literal["circle", "fill", "line"] = "line",
148
+ color: str = "#FF0000",
149
+ opacity: float = 1,
150
+ logical_op:str | None = None,
151
+ feature:str | None = None,
152
+ operator:str | None = None,
153
+ value:Union[str, float, float] | None = None
154
+ ):
155
+
156
+ """
157
+ Add a Vector Tile Layer to the document.
158
+
159
+ :param name: The name that will be used for the object in the document.
160
+ :param url: The tiles url.
161
+ :param source_layer: The source layer to use.
162
+ :param attribution: The attribution.
163
+ :param opacity: The opacity, between 0 and 1.
164
+ """
165
+ source_layers = get_source_layer_names(url)
166
+ if source_layer is None and len(source_layers) == 1:
167
+ source_layer = source_layers[0]
168
+ if source_layer not in source_layers:
169
+ raise ValueError(f'source_layer should be one of {source_layers}')
170
+
171
+ source = {
172
+ "type": SourceType.VectorTileSource,
173
+ "name": f"{name} Source",
174
+ "parameters": {
175
+ "url": url,
176
+ "minZoom": min_zoom,
177
+ "maxZoom": max_zoom,
178
+ "attribution": attribution,
179
+ "htmlAttribution": attribution,
180
+ "provider": "",
181
+ "bounds": [],
182
+ "urlParameters": {},
183
+ },
184
+ }
185
+
186
+ source_id = self._add_source(OBJECT_FACTORY.create_source(source, self))
187
+
188
+ layer = {
189
+ "type": LayerType.VectorTileLayer,
190
+ "name": name,
191
+ "visible": True,
192
+ "parameters": {
193
+ "source": source_id,
194
+ "type": type,
195
+ "opacity": opacity,
196
+ "sourceLayer": source_layer,
197
+ "color": color,
198
+ "opacity": opacity,
199
+ },
200
+ "filters": {
201
+ "appliedFilters": [
202
+ {
203
+ "feature": feature,
204
+ "operator": operator,
205
+ "value": value
206
+ }
207
+ ],
208
+ "logicalOp": logical_op
209
+ }
210
+ }
211
+
212
+ return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))
213
+
214
+ def add_geojson_layer(
215
+ self,
216
+ path: str | None = None,
217
+ data: Dict | None = None,
218
+ name: str = "GeoJSON Layer",
219
+ type: "circle" | "fill" | "line" = "line",
220
+ color: str = "#FF0000",
221
+ opacity: float = 1,
222
+ logical_op:str | None = None,
223
+ feature:str | None = None,
224
+ operator:str | None = None,
225
+ value:Union[str, number, float] | None = None
226
+ ):
227
+ """
228
+ Add a GeoJSON Layer to the document.
229
+
230
+ :param name: The name that will be used for the object in the document.
231
+ :param path: The path to the JSON file to embed into the jGIS file.
232
+ :param data: The raw GeoJSON data to embed into the jGIS file.
233
+ :param type: The type of the vector layer to create.
234
+ :param color: The color to apply to features.
235
+ :param opacity: The opacity, between 0 and 1.
236
+ """
237
+ if path is None and data is None:
238
+ raise ValueError("Cannot create a GeoJSON layer without data")
239
+
240
+ if path is not None and data is not None:
241
+ raise ValueError("Cannot set GeoJSON layer data and path at the same time")
242
+
243
+ if path is not None:
244
+ # We cannot put the path to the file in the model
245
+ # We don't know where the kernel runs/live
246
+ # The front-end would have no way of finding the file reliably
247
+ # TODO Support urls to JSON files, in that case, don't embed the data
248
+ with open(path, "r") as fobj:
249
+ parameters = {"data": json.loads(fobj.read())}
250
+
251
+ if data is not None:
252
+ parameters = {"data": data}
253
+
254
+ source = {
255
+ "type": SourceType.GeoJSONSource,
256
+ "name": f"{name} Source",
257
+ "parameters": parameters,
258
+ }
259
+
260
+ source_id = self._add_source(OBJECT_FACTORY.create_source(source, self))
261
+
262
+ layer = {
263
+ "type": LayerType.VectorLayer,
264
+ "name": name,
265
+ "visible": True,
266
+ "parameters": {
267
+ "source": source_id,
268
+ "type": type,
269
+ "color": color,
270
+ "opacity": opacity,
271
+ },
272
+ "filters": {
273
+ "appliedFilters": [
274
+ {
275
+ "feature": feature,
276
+ "operator": operator,
277
+ "value": value
278
+ }
279
+ ],
280
+ "logicalOp": logical_op
281
+ }
282
+ }
283
+
284
+ return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))
285
+
286
+ def add_image_layer(
287
+ self,
288
+ url: str,
289
+ coordinates: [],
290
+ name: str = "Image Layer",
291
+ opacity: float = 1,
292
+ ):
293
+ """
294
+ Add a Image Layer to the document.
295
+
296
+ :param name: The name that will be used for the object in the document.
297
+ :param url: The image url.
298
+ :param coordinates: Corners of image specified in longitude, latitude pairs.
299
+ :param opacity: The opacity, between 0 and 1.
300
+ """
301
+
302
+ if url is None or coordinates is None:
303
+ raise ValueError("URL and Coordinates are required")
304
+
305
+ source = {
306
+ "type": SourceType.ImageSource,
307
+ "name": f"{name} Source",
308
+ "parameters": {
309
+ "url": url,
310
+ "coordinates": coordinates
311
+ },
312
+ }
313
+
314
+ source_id = self._add_source(OBJECT_FACTORY.create_source(source, self))
315
+
316
+ layer = {
317
+ "type": LayerType.RasterLayer,
318
+ "name": name,
319
+ "visible": True,
320
+ "parameters": {"source": source_id, "opacity": opacity},
321
+ }
322
+
323
+ return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))
324
+
325
+ def add_video_layer(
326
+ self,
327
+ urls: [],
328
+ name: str = "Image Layer",
329
+ coordinates: [] = [],
330
+ opacity: float = 1,
331
+ ):
332
+ """
333
+ Add a Video Layer to the document.
334
+
335
+ :param name: The name that will be used for the object in the document.
336
+ :param urls: URLs to video content in order of preferred format.
337
+ :param coordinates: Corners of video specified in longitude, latitude pairs.
338
+ :param opacity: The opacity, between 0 and 1.
339
+ """
340
+
341
+ if urls is None or coordinates is None:
342
+ raise ValueError("URLs and Coordinates are required")
343
+
344
+ source = {
345
+ "type": SourceType.VideoSource,
346
+ "name": f"{name} Source",
347
+ "parameters": {
348
+ "urls": urls,
349
+ "coordinates": coordinates
350
+ },
351
+ }
352
+
353
+ source_id = self._add_source(OBJECT_FACTORY.create_source(source, self))
354
+
355
+ layer = {
356
+ "type": LayerType.RasterLayer,
357
+ "name": name,
358
+ "visible": True,
359
+ "parameters": {"source": source_id, "opacity": opacity},
360
+ }
361
+
362
+ return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))
363
+
364
+ def add_filter(self, layer_id: str, logical_op:str, feature:str, operator:str, value:Union[str, number, float]):
365
+ """
366
+ Add a filter to a layer
367
+
368
+ :param str layer_id: The ID of the layer to filter
369
+ :param str logical_op: The logical combination to apply to filters. Must be "any" or "all"
370
+ :param str feature: The feature to be filtered on
371
+ :param str operator: The operator used to compare the feature and value
372
+ :param Union[str, number, float] value: The value to be filtered on
373
+ """
374
+ layer = self._layers.get(layer_id)
375
+
376
+ # Check if the layer exists
377
+ if layer is None:
378
+ raise ValueError(f"No layer found with ID: {layer_id}")
379
+
380
+ # Initialize filters if it doesn't exist
381
+ if 'filters' not in layer:
382
+ layer['filters'] = {
383
+ 'appliedFilters': [
384
+ {
385
+ 'feature': feature,
386
+ 'operator': operator,
387
+ 'value': value
388
+ }
389
+ ],
390
+ 'logicalOp': logical_op}
391
+
392
+ self._layers[layer_id] = layer
393
+ return
394
+
395
+ # Add new filter
396
+ filters = layer['filters']
397
+ filters['appliedFilters'].append({'feature': feature, 'operator': operator, 'value': value})
398
+
399
+ # update the logical operation
400
+ filters['logicalOp'] = logical_op
401
+
402
+ self._layers[layer_id] = layer
403
+
404
+ def update_filter(self, layer_id: str, logical_op:str, feature:str, operator:str, value:Union[str, number, float]):
405
+ """
406
+ Update a filter applied to a layer
407
+
408
+ :param str layer_id: The ID of the layer to filter
409
+ :param str logical_op: The logical combination to apply to filters. Must be "any" or "all"
410
+ :param str feature: The feature to update the value for
411
+ :param str operator: The operator used to compare the feature and value
412
+ :param Union[str, number, float] value: The new value to be filtered on
413
+ """
414
+ layer = self._layers.get(layer_id)
415
+
416
+ # Check if the layer exists
417
+ if layer is None:
418
+ raise ValueError(f"No layer found with ID: {layer_id}")
419
+
420
+ if 'filters' not in layer:
421
+ raise ValueError(f"No filters applied to layer: {layer_id}")
422
+
423
+ # Find the feature within the layer
424
+ feature = next((f for f in layer['filters']['appliedFilters'] if f['feature'] == feature), None)
425
+ if feature is None:
426
+ raise ValueError(f"No feature found with ID: {feature} in layer: {layer_id}")
427
+ return
428
+
429
+ # Update the feature value
430
+ feature['value'] = value
431
+
432
+ # update the logical operation
433
+ layer['filters']['logicalOp'] = logical_op
434
+
435
+ self._layers[layer_id] = layer
436
+
437
+ def clear_filters(self, layer_id: str):
438
+ """
439
+ Clear filters on a layer
440
+
441
+ :param str layer_id: The ID of the layer to clear filters from
442
+ """
443
+ layer = self._layers.get(layer_id)
444
+
445
+ # Check if the layer exists
446
+ if layer is None:
447
+ raise ValueError(f"No layer found with ID: {layer_id}")
448
+
449
+ if 'filters' not in layer:
450
+ raise ValueError(f"No filters applied to layer: {layer_id}")
451
+
452
+ layer['filters']['appliedFilters'] = []
453
+ self._layers[layer_id] = layer
454
+
455
+ def _add_source(self, new_object: "JGISObject"):
456
+ _id = str(uuid4())
457
+ obj_dict = json.loads(new_object.json())
458
+ self._sources[_id] = obj_dict
459
+ return _id
460
+
461
+ def _add_layer(self, new_object: "JGISObject"):
462
+ _id = str(uuid4())
463
+ obj_dict = json.loads(new_object.json())
464
+ self._layers[_id] = obj_dict
465
+ self._layerTree.append(_id)
466
+ return _id
467
+
468
+ @classmethod
469
+ def _path_to_comm(cls, filePath: Optional[str]) -> Dict:
470
+ path = None
471
+ format = None
472
+ contentType = None
473
+
474
+ if filePath is not None:
475
+ path = normalize_path(filePath)
476
+ file_name = Path(path).name
477
+ try:
478
+ ext = file_name.split(".")[1].lower()
479
+ except Exception:
480
+ raise ValueError("Can not detect file extension!")
481
+ if ext == "jgis":
482
+ format = "text"
483
+ contentType = "jgis"
484
+ elif ext == "qgz":
485
+ format = "base64"
486
+ contentType = "QGZ"
487
+ elif ext == "qgs":
488
+ format = "base64"
489
+ contentType = "QGS"
490
+ else:
491
+ raise ValueError("File extension is not supported!")
492
+ return dict(
493
+ path=path, format=format, contentType=contentType, createydoc=path is None
494
+ )
495
+
496
+
497
+ class JGISLayer(BaseModel):
498
+ class Config:
499
+ arbitrary_types_allowed = True
500
+ extra = "allow"
501
+
502
+ name: str
503
+ type: LayerType
504
+ visible: bool
505
+ parameters: Union[
506
+ IRasterLayer,
507
+ IVectorLayer,
508
+ IVectorTileLayer,
509
+ IHillshadeLayer,
510
+ IImageLayer,
511
+ IWebGlLayer
512
+ ]
513
+ _parent = Optional[GISDocument]
514
+
515
+ def __init__(__pydantic_self__, parent, **data: Any) -> None: # noqa
516
+ super().__init__(**data)
517
+ __pydantic_self__._parent = parent
518
+
519
+
520
+ class JGISSource(BaseModel):
521
+ class Config:
522
+ arbitrary_types_allowed = True
523
+ extra = "allow"
524
+
525
+ name: str
526
+ type: SourceType
527
+ parameters: Union[
528
+ IRasterSource,
529
+ IVectorTileSource,
530
+ IGeoJSONSource,
531
+ IImageSource,
532
+ IVideoSource
533
+ ]
534
+ _parent = Optional[GISDocument]
535
+
536
+ def __init__(__pydantic_self__, parent, **data: Any) -> None: # noqa
537
+ super().__init__(**data)
538
+ __pydantic_self__._parent = parent
539
+
540
+
541
+ class SingletonMeta(type):
542
+ _instances = {}
543
+
544
+ def __call__(cls, *args, **kwargs):
545
+ if cls not in cls._instances:
546
+ instance = super().__call__(*args, **kwargs)
547
+ cls._instances[cls] = instance
548
+ return cls._instances[cls]
549
+
550
+
551
+ class ObjectFactoryManager(metaclass=SingletonMeta):
552
+ def __init__(self):
553
+ self._factories: Dict[str, type[BaseModel]] = {}
554
+
555
+ def register_factory(self, shape_type: str, cls: type[BaseModel]) -> None:
556
+ if shape_type not in self._factories:
557
+ self._factories[shape_type] = cls
558
+
559
+ def create_layer(
560
+ self, data: Dict, parent: Optional[GISDocument] = None
561
+ ) -> Optional[JGISLayer]:
562
+ object_type = data.get("type", None)
563
+ name: str = data.get("name", None)
564
+ visible: str = data.get("visible", True)
565
+ filters = data.get("filters", None)
566
+ if object_type and object_type in self._factories:
567
+ Model = self._factories[object_type]
568
+ args = {}
569
+ params = data["parameters"]
570
+ for field in Model.__fields__:
571
+ args[field] = params.get(field, None)
572
+ obj_params = Model(**args)
573
+ return JGISLayer(
574
+ parent=parent,
575
+ name=name,
576
+ visible=visible,
577
+ type=object_type,
578
+ parameters=obj_params,
579
+ filters=filters
580
+ )
581
+
582
+ return None
583
+
584
+ def create_source(
585
+ self, data: Dict, parent: Optional[GISDocument] = None
586
+ ) -> Optional[JGISSource]:
587
+ object_type = data.get("type", None)
588
+ name: str = data.get("name", None)
589
+ if object_type and object_type in self._factories:
590
+ Model = self._factories[object_type]
591
+ args = {}
592
+ params = data["parameters"]
593
+ for field in Model.__fields__:
594
+ args[field] = params.get(field, None)
595
+ obj_params = Model(**args)
596
+ return JGISSource(
597
+ parent=parent, name=name, type=object_type, parameters=obj_params
598
+ )
599
+
600
+ return None
601
+
602
+
603
+ OBJECT_FACTORY = ObjectFactoryManager()
604
+
605
+ OBJECT_FACTORY.register_factory(LayerType.RasterLayer, IRasterLayer)
606
+ OBJECT_FACTORY.register_factory(LayerType.VectorLayer, IVectorLayer)
607
+ OBJECT_FACTORY.register_factory(LayerType.VectorTileLayer, IVectorTileLayer)
608
+ OBJECT_FACTORY.register_factory(LayerType.HillshadeLayer, IHillshadeLayer)
609
+ OBJECT_FACTORY.register_factory(LayerType.WebGlLayer, IWebGlLayer)
610
+ OBJECT_FACTORY.register_factory(LayerType.ImageLayer, IImageLayer)
611
+
612
+ OBJECT_FACTORY.register_factory(SourceType.VectorTileSource, IVectorTileSource)
613
+ OBJECT_FACTORY.register_factory(SourceType.RasterSource, IRasterSource)
614
+ OBJECT_FACTORY.register_factory(SourceType.GeoJSONSource, IGeoJSONSource)
615
+ OBJECT_FACTORY.register_factory(SourceType.ImageSource, IImageSource)
616
+ OBJECT_FACTORY.register_factory(SourceType.VideoSource, IVideoSource)
@@ -0,0 +1,14 @@
1
+ from ._schema.jgis import * # noqa
2
+
3
+ from ._schema.rasterlayer import IRasterLayer # noqa
4
+ from ._schema.vectorlayer import IVectorLayer # noqa
5
+ from ._schema.vectorTileLayer import IVectorTileLayer # noqa
6
+ from ._schema.hillshadeLayer import IHillshadeLayer # noqa
7
+ from ._schema.imageLayer import IImageLayer # noqa
8
+ from ._schema.webGlLayer import IWebGlLayer # noqa
9
+
10
+ from ._schema.vectortilesource import IVectorTileSource # noqa
11
+ from ._schema.rastersource import IRasterSource # noqa
12
+ from ._schema.geojsonsource import IGeoJSONSource # noqa
13
+ from ._schema.videoSource import IVideoSource # noqa
14
+ from ._schema.imageSource import IImageSource # noqa
@@ -0,0 +1,3 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: schema
3
+ # timestamp: 2024-09-13T09:04:30+00:00
@@ -0,0 +1,24 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: geoTiffSource.json
3
+ # timestamp: 2024-09-13T09:04:30+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import List, Optional
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class Url(BaseModel):
13
+ url: Optional[str] = None
14
+ min: Optional[float] = None
15
+ max: Optional[float] = None
16
+
17
+
18
+ class IGeoTiffSource(BaseModel):
19
+ model_config = ConfigDict(
20
+ extra='forbid',
21
+ )
22
+ urls: List[Url] = Field(..., description='URLs', min_length=1)
23
+ normalize: Optional[bool] = True
24
+ wrapX: Optional[bool] = False