aceti 0.0.2__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.
- aceti/__init__.py +3 -0
- aceti/map_creator.py +273 -0
- aceti/map_importer.py +107 -0
- aceti/maps/alamillo30x49latlon.npy +0 -0
- aceti/maps/alamillo30x49mask.npy +0 -0
- aceti/maps/alamillo95x216latlon.npy +0 -0
- aceti/maps/alamillo95x216mask.npy +0 -0
- aceti/maps/alamilloaccess11x15latlon.npy +0 -0
- aceti/maps/alamilloaccess11x15mask.npy +0 -0
- aceti/old/create grid.py +573 -0
- aceti/old/gen_lat_lon_map.py +38 -0
- aceti/old/generate_map.py +40 -0
- aceti/old/legendarium.py +180 -0
- aceti/old/maps_utils.py +138 -0
- aceti/old/path_planner_checker.py +65 -0
- aceti-0.0.2.dist-info/METADATA +44 -0
- aceti-0.0.2.dist-info/RECORD +19 -0
- aceti-0.0.2.dist-info/WHEEL +5 -0
- aceti-0.0.2.dist-info/licenses/LICENSE +19 -0
aceti/__init__.py
ADDED
aceti/map_creator.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
from aceti import import_map
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from PIL import Image
|
|
5
|
+
import math
|
|
6
|
+
from tqdm.auto import tqdm
|
|
7
|
+
import os
|
|
8
|
+
import urllib.request
|
|
9
|
+
import pyproj
|
|
10
|
+
|
|
11
|
+
class GoogleMapDownloader:
|
|
12
|
+
"""
|
|
13
|
+
A class which generates high resolution google maps image given
|
|
14
|
+
Northwest and Southeast points in [Lat Lon] and zoom level
|
|
15
|
+
You can change the quality of the image by changing the zoom and layer values
|
|
16
|
+
|
|
17
|
+
19 is a valid zoom value knowing the size of our drone
|
|
18
|
+
|
|
19
|
+
for the maps you have available
|
|
20
|
+
ROADMAP = "v"
|
|
21
|
+
TERRAIN = "p"
|
|
22
|
+
ALTERED_ROADMAP = "r"
|
|
23
|
+
SATELLITE = "s"
|
|
24
|
+
TERRAIN_ONLY = "t"
|
|
25
|
+
HYBRID = "y"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, Northwest, Southeast, zoom=19, layer="s", server="google"):
|
|
31
|
+
"""
|
|
32
|
+
GoogleMapDownloader Constructor
|
|
33
|
+
Args:
|
|
34
|
+
lat: The latitude of the location required
|
|
35
|
+
lng: The longitude of the location required
|
|
36
|
+
zoom: The zoom level of the location required, ranges from 0 - 23
|
|
37
|
+
"""
|
|
38
|
+
self.Northwest = Northwest
|
|
39
|
+
self.Southeast = Southeast
|
|
40
|
+
if zoom < 0 or zoom > 22:
|
|
41
|
+
raise ValueError("Zoom level must be between 0 and 22")
|
|
42
|
+
self._zoom = zoom
|
|
43
|
+
self._layer = layer
|
|
44
|
+
|
|
45
|
+
if server not in ["google", "arcgis"]:
|
|
46
|
+
raise ValueError("Server must be either 'google' or 'arcgis'")
|
|
47
|
+
self._server = server
|
|
48
|
+
|
|
49
|
+
# Set up transformers, EPSG:3857 is metric, same as EPSG:900913
|
|
50
|
+
self.to_proxy_transformer = pyproj.Transformer.from_crs('epsg:4326', 'epsg:3857')
|
|
51
|
+
self.to_original_transformer = pyproj.Transformer.from_crs('epsg:3857', 'epsg:4326')
|
|
52
|
+
|
|
53
|
+
# Check if the coordinates are valid
|
|
54
|
+
if not (isinstance(Northwest, np.ndarray) or isinstance(Northwest, list)) or not (isinstance(Southeast, np.ndarray) or isinstance(Southeast, list)):
|
|
55
|
+
raise ValueError("Northwest and Southeast must be tuples")
|
|
56
|
+
if Northwest[0]<=Southeast[0] or Northwest[1]>=Southeast[1]:
|
|
57
|
+
raise ValueError("you didn't provide a valid rectangle, check your coordinates")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
self.get_tile_coordinates()
|
|
61
|
+
|
|
62
|
+
if (abs(self.nw_tile[0]-self.se_tile[0]) == 0) or (abs(self.nw_tile[1]-self.se_tile[1]) == 0):
|
|
63
|
+
raise ValueError("Insuficient zoom, points are too close, i am lazy to program this, increase zoom")
|
|
64
|
+
self.generateImage()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_tile_coordinates(self):
|
|
68
|
+
"""
|
|
69
|
+
Generates an X,Y tile and pixel coordinate based on the latitude, longitude
|
|
70
|
+
and zoom level
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
tile_size = 256 #each tile has 256 pixels
|
|
74
|
+
|
|
75
|
+
# Use a left shift to get the power of 2, zoom 0 is a world map, zoom 1 is wold map divided in 4 tiles, zoom 2 is world map divided in 16 tiles
|
|
76
|
+
# i.e. a zoom level of 2 will have 2^2 = 4 tiles as coordinate sistem divides axis in 4 (x+y+,x-y+,x-y-,x+y-)
|
|
77
|
+
numTiles = 1 << self._zoom
|
|
78
|
+
|
|
79
|
+
########################
|
|
80
|
+
# For Northwest corner
|
|
81
|
+
########################
|
|
82
|
+
# Find the x_pixel given the longitude
|
|
83
|
+
x_pixel = (tile_size / 2 + self.Northwest[1] * tile_size / 360.0) * numTiles
|
|
84
|
+
# Convert the latitude to radians and take the sine
|
|
85
|
+
sin_y = math.sin(self.Northwest[0] * (math.pi / 180.0))
|
|
86
|
+
# Calulate the y_pixel
|
|
87
|
+
y_pixel = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) * -(tile_size / (2 * math.pi))) * numTiles
|
|
88
|
+
|
|
89
|
+
self.nw_pixel=[x_pixel, y_pixel]
|
|
90
|
+
self.nw_tile=[int(x_pixel // tile_size), int(y_pixel // tile_size)]
|
|
91
|
+
|
|
92
|
+
########################
|
|
93
|
+
# For Southeast corner
|
|
94
|
+
########################
|
|
95
|
+
# Find the x_pixel given the longitude
|
|
96
|
+
x_pixel = (tile_size / 2 + self.Southeast[1] * tile_size / 360.0) * numTiles
|
|
97
|
+
# Convert the latitude to radians and take the sine
|
|
98
|
+
sin_y = math.sin(self.Southeast[0] * (math.pi / 180.0))
|
|
99
|
+
# Calulate the y_pixel
|
|
100
|
+
y_pixel = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) * -(tile_size / (2 * math.pi))) * numTiles
|
|
101
|
+
|
|
102
|
+
self.se_pixel=[x_pixel, y_pixel]
|
|
103
|
+
self.se_tile=[math.ceil(x_pixel // tile_size)+1, math.ceil(y_pixel // tile_size)+1]
|
|
104
|
+
|
|
105
|
+
def generateImage(self):
|
|
106
|
+
"""
|
|
107
|
+
Generates an image by stitching a number of google map tiles together. after executing self.get_tile_coordinates
|
|
108
|
+
Returns:
|
|
109
|
+
A high-resolution Goole Map image.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
tile_width = abs(self.nw_tile[0]-self.se_tile[0])
|
|
113
|
+
tile_height = abs(self.nw_tile[1]-self.se_tile[1])
|
|
114
|
+
|
|
115
|
+
# Determine the size of the image, for simplicity, we will print the whole image and later crop it
|
|
116
|
+
width, height = 256 * tile_width, 256 * tile_height
|
|
117
|
+
|
|
118
|
+
# Create a new image of the size required
|
|
119
|
+
map_img = Image.new('RGB', (width, height))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# calculate northwest pixel of the tile
|
|
123
|
+
start_x=self.nw_tile[0]
|
|
124
|
+
start_y=self.nw_tile[1]
|
|
125
|
+
|
|
126
|
+
# print the map and report using tdqm
|
|
127
|
+
pbar = tqdm(total=tile_width*tile_height, unit="tiles")
|
|
128
|
+
pbar.set_description("Downloading map")
|
|
129
|
+
pbar.set_postfix_str(f"Zoom: {self._zoom}, Layer: {self._layer}")
|
|
130
|
+
pbar.refresh()
|
|
131
|
+
for x in range(0, tile_width):
|
|
132
|
+
for y in range(0, tile_height):
|
|
133
|
+
current_tile = str(x) + '-' + str(y)
|
|
134
|
+
try:
|
|
135
|
+
if self._server == "google":
|
|
136
|
+
url = f'https://mt0.google.com/vt?lyrs={self._layer}&x=' + str(start_x + x) + '&y=' + str(start_y + y) + '&z=' + str(self._zoom)
|
|
137
|
+
urllib.request.urlretrieve(url, current_tile)
|
|
138
|
+
elif self._server == "arcgis":
|
|
139
|
+
url = f"http://services.arcgisonline.com/ArcGis/rest/services/World_Imagery/MapServer/tile/{self._zoom}/{start_y + y}/{start_x + x}.png"
|
|
140
|
+
urllib.request.urlretrieve(url, current_tile)
|
|
141
|
+
else:
|
|
142
|
+
raise ValueError("Server not available")
|
|
143
|
+
except :
|
|
144
|
+
raise ValueError("Server not available")
|
|
145
|
+
im = Image.open(current_tile)
|
|
146
|
+
map_img.paste(im, (x * 256, y * 256))
|
|
147
|
+
|
|
148
|
+
os.remove(current_tile)
|
|
149
|
+
pbar.update(1)
|
|
150
|
+
pbar.refresh()
|
|
151
|
+
|
|
152
|
+
print("map downloaded")
|
|
153
|
+
|
|
154
|
+
self.raw_map_img = map_img.copy()
|
|
155
|
+
# cut corners
|
|
156
|
+
left=abs(self.nw_tile[0] * 256 - self.nw_pixel[0])
|
|
157
|
+
top=abs(self.nw_tile[1] * 256 - self.nw_pixel[1])
|
|
158
|
+
right=left + abs(self.se_pixel[0] - self.nw_pixel[0])
|
|
159
|
+
bottom=top + abs(self.nw_pixel[1] - self.se_pixel[1])
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
map_img = map_img.crop((left, top, right, bottom))
|
|
163
|
+
self.map_img = map_img
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def to_pixel(self, GPS_point):
|
|
167
|
+
"""
|
|
168
|
+
Generates an X,Y pixel coordinate based on the latitude, longitude
|
|
169
|
+
and zoom level
|
|
170
|
+
Returns: An X,Y pixel coordinate
|
|
171
|
+
"""
|
|
172
|
+
#check if we are inside the map
|
|
173
|
+
self.pole=[None,None]
|
|
174
|
+
if self.Northwest[0]>self.Southeast[0]:
|
|
175
|
+
self.pole[0]="N"
|
|
176
|
+
else:
|
|
177
|
+
self.pole[0]="S"
|
|
178
|
+
if self.Northwest[1]>self.Southeast[1]:
|
|
179
|
+
self.pole[1]="W"
|
|
180
|
+
else:
|
|
181
|
+
self.pole[1]="E"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if self.pole[1]=="W":
|
|
185
|
+
if ((self.Southeast[1])>(GPS_point[1])>(self.Northwest[1])):
|
|
186
|
+
raise ValueError(f"point {GPS_point} 1W is outside the map {[self.Northwest,self.Southeast]}")
|
|
187
|
+
else:
|
|
188
|
+
if ((self.Southeast[1])<(GPS_point[1])<(self.Northwest[1])):
|
|
189
|
+
raise ValueError(f"point {GPS_point} 1E is outside the map {[self.Northwest,self.Southeast]}")
|
|
190
|
+
if self.pole[0]=="N":
|
|
191
|
+
if ((self.Southeast[0])>(GPS_point[0])>(self.Northwest[0])):
|
|
192
|
+
raise ValueError(f"point {GPS_point} 0N is outside the map {[self.Northwest,self.Southeast]}")
|
|
193
|
+
else:
|
|
194
|
+
if ((self.Southeast[0])<(GPS_point[0])<(self.Northwest[0])):
|
|
195
|
+
raise ValueError(f"point {GPS_point} 0S is outside the map {[self.Northwest,self.Southeast]}")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
tile_size = 256 #each tile has 256 pixels
|
|
200
|
+
|
|
201
|
+
# Use a left shift to get the power of 2, zoom 0 is a world map, zoom 1 is wold map divided in 4 tiles, zoom 2 is world map divided in 16 tiles
|
|
202
|
+
# i.e. a zoom level of 2 will have 2^2 = 4 tiles as coordinate sistem divides axis in 4 (x+y+,x-y+,x-y-,x+y-)
|
|
203
|
+
numTiles = 1 << self._zoom
|
|
204
|
+
|
|
205
|
+
# Find the x_pixel given the longitude
|
|
206
|
+
x_pixel = (tile_size / 2 + GPS_point[1] * tile_size / 360.0) * numTiles
|
|
207
|
+
|
|
208
|
+
# Convert the latitude to radians and take the sine
|
|
209
|
+
sin_y = math.sin(GPS_point[0] * (math.pi / 180.0))
|
|
210
|
+
|
|
211
|
+
# Calulate the y_pixel
|
|
212
|
+
y_pixel = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) * -(
|
|
213
|
+
tile_size / (2 * math.pi))) * numTiles
|
|
214
|
+
|
|
215
|
+
return [x_pixel-self.nw_pixel[0], y_pixel-self.nw_pixel[1]]
|
|
216
|
+
|
|
217
|
+
def show_grid(self, lat_lon_map, base_matrix):
|
|
218
|
+
|
|
219
|
+
# if lat_lon_map.shape[0] == base_matrix.shape[0] and lat_lon_map.shape[1] == base_matrix.shape[1]:
|
|
220
|
+
# print("base_matrix and lat_lon_map have the same shape, extending last column and row")
|
|
221
|
+
# # Extend the last column and row of lat_lon_map
|
|
222
|
+
# lat_lon_map = np.concatenate((lat_lon_map, lat_lon_map[-1:, :]), axis=0)
|
|
223
|
+
# lat_lon_map = np.concatenate((lat_lon_map, lat_lon_map[:, -1:]), axis=1)
|
|
224
|
+
# for i in range(lat_lon_map.shape[0]):
|
|
225
|
+
# lat_lon_map[i, -1] = lat_lon_map[i, -2] +( lat_lon_map[i, -2] - lat_lon_map[i, -3])
|
|
226
|
+
# for i in range(lat_lon_map.shape[1]):
|
|
227
|
+
# lat_lon_map[-1, i] = lat_lon_map[-2, i] + (lat_lon_map[-2, i] - lat_lon_map[-3, i])
|
|
228
|
+
# print("lat_lon_map shape: ", lat_lon_map.shape)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# cut corners
|
|
232
|
+
p1=self.to_pixel(lat_lon_map[0, 0])
|
|
233
|
+
p2=self.to_pixel(lat_lon_map[1, 1])
|
|
234
|
+
distance=min(abs(p1[0]-p2[0]),abs(p1[1]-p2[1]))
|
|
235
|
+
left=abs(self.nw_tile[0] * 256 - self.nw_pixel[0] -distance)
|
|
236
|
+
top=abs(self.nw_tile[1] * 256 - self.nw_pixel[1] +distance)
|
|
237
|
+
right=left + abs(self.se_pixel[0] - self.nw_pixel[0] + distance)
|
|
238
|
+
bottom=top + abs(self.nw_pixel[1] - self.se_pixel[1] -distance)
|
|
239
|
+
print("distance: ", distance)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
map_img = self.raw_map_img.crop((left, top, right, bottom))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
plt.figure()
|
|
246
|
+
plt.imshow(self.map_img, cmap=plt.get_cmap('binary'))
|
|
247
|
+
#obtain xy points of the base matrix
|
|
248
|
+
points = np.argwhere(base_matrix == 1)
|
|
249
|
+
|
|
250
|
+
for point in points:
|
|
251
|
+
try:
|
|
252
|
+
x=[]
|
|
253
|
+
y=[]
|
|
254
|
+
center = self.to_pixel(lat_lon_map[point[0], point[1]])
|
|
255
|
+
p1=[center[0]-distance/2, center[1]-distance/2]
|
|
256
|
+
p2=[center[0]+distance/2, center[1]-distance/2]
|
|
257
|
+
p3=[center[0]-distance/2, center[1]+distance/2]
|
|
258
|
+
p4=[center[0]+distance/2, center[1]+distance/2]
|
|
259
|
+
x.append(p1[0])
|
|
260
|
+
y.append(p1[1])
|
|
261
|
+
x.append(p2[0])
|
|
262
|
+
y.append(p2[1])
|
|
263
|
+
x.append(p4[0])
|
|
264
|
+
y.append(p4[1])
|
|
265
|
+
x.append(p3[0])
|
|
266
|
+
y.append(p3[1])
|
|
267
|
+
x.append(p1[0])
|
|
268
|
+
y.append(p1[1])
|
|
269
|
+
plt.plot(x,y,'b-', linewidth=distance/20)
|
|
270
|
+
except Exception as e:
|
|
271
|
+
continue
|
|
272
|
+
plt.savefig("map.png", dpi=400)
|
|
273
|
+
plt.show()
|
aceti/map_importer.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from matplotlib import pyplot as plt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
if sys.version_info < (3, 9):
|
|
8
|
+
# importlib.resources either doesn't exist or lacks the files() function, so use the PyPI version:
|
|
9
|
+
import importlib_resources
|
|
10
|
+
else:
|
|
11
|
+
# importlib.resources has files(), so use that:
|
|
12
|
+
import importlib.resources as importlib_resources
|
|
13
|
+
pkg = importlib_resources.files("aceti")
|
|
14
|
+
|
|
15
|
+
def import_map(map_name: str):
|
|
16
|
+
"""
|
|
17
|
+
Import a map from a file and return the base matrix and lat/lon map.
|
|
18
|
+
Args:
|
|
19
|
+
map_name (str): The name of the map to import.
|
|
20
|
+
Returns:
|
|
21
|
+
tuple: A tuple containing the base matrix and lat/lon map.
|
|
22
|
+
"""
|
|
23
|
+
map_name = map_name.lower()
|
|
24
|
+
if os.path.exists(pkg.joinpath("maps", f"{map_name}mask.npy")):
|
|
25
|
+
print(f"Loading map {map_name} from package directory")
|
|
26
|
+
else:
|
|
27
|
+
raise FileNotFoundError(f"Map {map_name} not found in package directory")
|
|
28
|
+
|
|
29
|
+
base_matrix = np.load(pkg.joinpath("maps", f"{map_name}mask.npy"))
|
|
30
|
+
|
|
31
|
+
# Load the lat/lon map
|
|
32
|
+
lat_lon_map = np.load(pkg.joinpath("maps", f"{map_name}latlon.npy"))
|
|
33
|
+
|
|
34
|
+
return base_matrix, lat_lon_map
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def plot_map(map_name):
|
|
38
|
+
''' Convert a CSV file to a binary image. '''
|
|
39
|
+
if isinstance(map_name, str):
|
|
40
|
+
binary_image = np.load(pkg.joinpath("maps", f"{map_name}mask.npy"))
|
|
41
|
+
elif isinstance(map_name, np.ndarray):
|
|
42
|
+
#check map is 1 and 0
|
|
43
|
+
if np.all(np.isin(map_name, [0, 1])):
|
|
44
|
+
binary_image = map_name
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError("map_name must be a string or a numpy array of 0s and 1s")
|
|
47
|
+
else:
|
|
48
|
+
raise ValueError("map_name must be a string or a numpy array")
|
|
49
|
+
# Load the CSV file
|
|
50
|
+
binary_image = np.load(pkg.joinpath("maps", f"{map_name}mask.npy"))
|
|
51
|
+
|
|
52
|
+
# Convert the binary image to 0s and 255s
|
|
53
|
+
binary_image = np.where(binary_image > 0, 255, 0)
|
|
54
|
+
|
|
55
|
+
plt.imshow(binary_image, cmap='gray')
|
|
56
|
+
plt.axis('off')
|
|
57
|
+
plt.show()
|
|
58
|
+
|
|
59
|
+
def map_downsize(map_name, factor=2):
|
|
60
|
+
''' Downsize a map. '''
|
|
61
|
+
if isinstance(map_name, str):
|
|
62
|
+
binary_image = np.load(pkg.joinpath("maps", f"{map_name}mask.npy"))
|
|
63
|
+
elif isinstance(map_name, np.ndarray):
|
|
64
|
+
binary_image = map_name
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError("map_name must be a string or a numpy array")
|
|
67
|
+
# Downsize the binary image
|
|
68
|
+
return binary_image[::factor, ::factor]
|
|
69
|
+
|
|
70
|
+
def csv_shrink(map_name, init_column=0, final_column=None, init_row=0, final_row=None):
|
|
71
|
+
''' Shrink a map. '''
|
|
72
|
+
if isinstance(map_name, str):
|
|
73
|
+
binary_image = np.load(pkg.joinpath("maps", f"{map_name}mask.npy"))
|
|
74
|
+
elif isinstance(map_name, np.ndarray):
|
|
75
|
+
binary_image = map_name
|
|
76
|
+
else:
|
|
77
|
+
raise ValueError("map_name must be a string or a numpy array")
|
|
78
|
+
|
|
79
|
+
# Set the final column and row if they are None
|
|
80
|
+
if final_column is None:
|
|
81
|
+
final_column = binary_image.shape[1]
|
|
82
|
+
if final_row is None:
|
|
83
|
+
final_row = binary_image.shape[0]
|
|
84
|
+
|
|
85
|
+
# Shrink the binary image
|
|
86
|
+
return binary_image[init_row:final_row, init_column:final_column]
|
|
87
|
+
|
|
88
|
+
def plot_grid(map):
|
|
89
|
+
''' Plot a grid on top of a map. '''
|
|
90
|
+
|
|
91
|
+
#check map properties
|
|
92
|
+
if isinstance(map, str):
|
|
93
|
+
base_matrix, lat_lon_map = import_map(map)
|
|
94
|
+
elif isinstance(map, tuple):
|
|
95
|
+
base_matrix, lat_lon_map = map
|
|
96
|
+
if isinstance(base_matrix, np.ndarray) and isinstance(lat_lon_map, np.ndarray):
|
|
97
|
+
if not np.all(np.isin(base_matrix, [0, 1])):
|
|
98
|
+
raise ValueError("base_matrix must be a numpy array of 0s and 1s")
|
|
99
|
+
if base_matrix.shape != lat_lon_map.shape:
|
|
100
|
+
raise ValueError("base_matrix and lat_lon_map must have the same shape")
|
|
101
|
+
if not np.all(np.apply_along_axis(lambda x: isinstance(x, tuple) and len(x) == 2 and all(isinstance(i, float) for i in x), 1, lat_lon_map)):
|
|
102
|
+
raise ValueError("lat_lon_map must be a numpy array of tuples of 2 coordinates, [lat, lon]")
|
|
103
|
+
else:
|
|
104
|
+
raise ValueError("map must be a string or a tuple of [base_matrix, lat_lon_map]")
|
|
105
|
+
else:
|
|
106
|
+
raise ValueError("map must be a string or a tuple of [base_matrix, lat_lon_map]")
|
|
107
|
+
# Plot the map
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|