gemf-map 0.3.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.
gemf/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """
2
+ A Python package for the GEMF map format.
3
+ """
4
+
5
+ from .gemf import GEMF
gemf/dump.py ADDED
@@ -0,0 +1,124 @@
1
+ def lonlat2xy(lon, lat, zoom):
2
+ """Convert longitude and latitude to x and y coordinates of a tile."""
3
+ lat_rad = math.radians(lat)
4
+ n = 2 ** zoom
5
+ xtile = int((lon + 180.0) / 360.0 * n)
6
+ ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n) # asinh(tan(x)) = log(tan(x) + sec(x))
7
+ return xtile, ytile
8
+
9
+ def filter_tiles_between(z, xmin, ymin, xmax, ymax):
10
+ """Get all tiles in a rectangular area."""
11
+ return sorted([(z, x_, y_) for x_, y_ in itertools.product(range(xmin, xmax+1), range(ymin, ymax+1))])
12
+
13
+
14
+ # class TileCollection
15
+ @staticmethod
16
+ def grid_idx2xy(z: int, idx: int):
17
+ idx = validate_int(idx)
18
+ if (idx >= 2**(2*z)) and (idx >= 0): raise IndexError(f"At zoom {z}, index {idx} out of bounds for number of tiles {2**(2*z)}")
19
+ y, x = divmod(idx, n:=2**z)
20
+ return x, y
21
+
22
+ # class TileRange
23
+ @classmethod
24
+ def from_limits(cls, z: int, xmin: int, xmax: int, ymin: int, ymax: int, tile: Type[TileBase] = TileBase, sort: bool = True, *args, **kwargs) -> None:
25
+ """"""
26
+ z, xmin, xmax, ymin, ymax = validate_int([z, xmin, xmax, ymin, ymax])
27
+
28
+ xs = [x for x in range(xmin, xmax+1)]
29
+ ys = [y for y in range(ymin, ymax+1)]
30
+
31
+ tiles = [tile(z, x, y, *args, **kwargs) for x, y in itertools.product(xs, ys)]
32
+ return cls(z, tiles, sort)
33
+
34
+
35
+
36
+
37
+ class GEMFTileBase:
38
+ """
39
+ Base class representing a single tile in the GEMF ecosystem.
40
+ Intended to be used as a base class in combination with classes inheriting from `TileBase`.
41
+ """
42
+ def __init__(self, verbose: bool = VERBOSE) -> None:
43
+ self._verbose = verbose
44
+
45
+ def __len__(self) -> int:
46
+ return len(self.load_bytes())
47
+
48
+ def to_dict(self) -> str: return str(self)
49
+
50
+ def write(self, f):
51
+ if self._verbose: print("Writing data...")
52
+ data_bytes = self.load_bytes()
53
+ f.write(data_bytes)
54
+
55
+
56
+
57
+ class GEMFDataSectionOld(GEMFList):
58
+ """
59
+ Sequential aggregate of encoded tile data as specified in section '4. Data Area' of the format specification.
60
+
61
+ This class is lazy, i.e. the tile data is not stored into memory upon creation. It only serves as a utility
62
+ """
63
+ def __init__(self, data: list[GEMFTile | GEMFBufferTile]) -> None:
64
+ super().__init__(data)
65
+
66
+
67
+ import inspect
68
+
69
+ def kwargify(loc):
70
+ curr_init = getattr(loc["__class__"], "__init__")
71
+ params = inspect.signature(curr_init).parameters
72
+ kwargs = {key: loc[key] for key in params if key != "self"}
73
+ return kwargs
74
+
75
+ class A:
76
+ def __init__(self, a, **kwargs) -> None: # A needs to accept kwargs to pass down the mro() chain
77
+ print("init A", locals())
78
+ self.a = a
79
+ super().__init__(**kwargs) # A is not end-of-inheritance: kwargs important to pass parameters down mro() chain
80
+
81
+ class B:
82
+ def __init__(self, b, **kwargs) -> None: # B is "end of inheritance chain" -> **kwargs to "swallow" excess parameters, leave if excess parameters are not supported
83
+ print("init B", locals())
84
+ self.b = b
85
+ super().__init__() # B is "end of inheritance chain" -> does not need to pass on kwargs
86
+
87
+ class C(A, B):
88
+ def __init__(self, a, b) -> None:
89
+ print("init C")
90
+ super().__init__(**kwargify(locals())) # all parameters have to be consumed (rest will be passed to object.__init__)
91
+
92
+ print("mro:", C.mro())
93
+ c = C(1, 2)
94
+ print(c.__dict__)
95
+ print("a:", c.a)
96
+ print("b:", c.b)
97
+
98
+
99
+ # class GEMF
100
+ def _read_datasection(self, f, headerinfo: HeaderInfo, sourcedata: SourceData, rangedata: RangeData, rangedetails: RangeDetails):
101
+ ADDRESS = len(headerinfo) + len(sourcedata) + len(rangedata)
102
+ f.seek(ADDRESS)
103
+
104
+ data = GEMFDataSection([])
105
+ for rangedetail in rangedetails:
106
+ f.seek(rangedetail.address)
107
+ tile_bytes = f.read(rangedetail.length)
108
+
109
+ range_idx = rangedetail._range_idx
110
+ tile_idx = rangedetail._tile_idx
111
+
112
+ range_ = rangedata[range_idx]
113
+
114
+ z, x, y = range_.get_zxy(tile_idx)
115
+ tile = GEMFBufferTile(z, x, y, range_idx, tile_idx, tile_bytes)
116
+ data.append(tile)
117
+
118
+ return data
119
+
120
+ def validate_int(values: Union[List[Any], Any]):
121
+ """If possible, value(s) will be cast to a Python int. If not, will raise a `ValueError`."""
122
+ if not (isinstance(values, int) or all([isinstance(val_, int) for val_ in values])):
123
+ raise ValueError(f"All values must be of type `int`; types: {[type(val) for val in values]}")
124
+ return values