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 +5 -0
- gemf/dump.py +124 -0
- gemf/gemf.py +916 -0
- gemf/gemf_dump.py +806 -0
- gemf/tiles.py +510 -0
- gemf/utils.py +100 -0
- gemf_map-0.3.0.dist-info/METADATA +61 -0
- gemf_map-0.3.0.dist-info/RECORD +11 -0
- gemf_map-0.3.0.dist-info/WHEEL +5 -0
- gemf_map-0.3.0.dist-info/licenses/LICENSE +674 -0
- gemf_map-0.3.0.dist-info/top_level.txt +1 -0
gemf/__init__.py
ADDED
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
|