Skip to content

Commit b8ec095

Browse files
committed
add data_path to support mbtile rasters with an extra data layer
1 parent 1aa46e2 commit b8ec095

File tree

11 files changed

+75
-44
lines changed

11 files changed

+75
-44
lines changed

server/guppy/db/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class LayerMetadata(Base):
1515
layer_name = Column(String, nullable=False, unique=True)
1616
label = Column(String, nullable=False)
1717
file_path = Column(String, nullable=False)
18+
data_path = Column(String, nullable=False)
1819
is_rgb = Column(Boolean, nullable=False, default=False, server_default=text('FALSE'))
1920
is_mbtile = Column(Boolean, nullable=False, default=False, server_default=text('FALSE'))
2021

server/guppy/db/schemas.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class LayerMetadataSchema(CamelModel):
2121
layer_name: str
2222
label: Optional[str] = None
2323
file_path: str
24+
data_path: Optional[str] = None
2425
is_rgb: Optional[bool] = False
2526
is_mbtile: Optional[bool] = False
2627

@@ -199,6 +200,7 @@ class LayerMetadataBody(CamelModel):
199200
layer_name: str
200201
label: str
201202
file_path: str
203+
data_path: Optional[str] = None
202204
is_rgb: Optional[bool] = False
203205
is_mbtile: Optional[bool] = False
204206

server/guppy/endpoints/endpoint_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ def validate_layer_and_get_file_path(db: Session, layer_name: str) -> str:
204204
file_path = layer.file_path
205205
if not os.path.exists(file_path):
206206
raise HTTPException(status_code=404, detail=f"File not found: {file_path}")
207+
if file_path.endswith('.mbtiles') and os.path.exists(layer.data_path):
208+
file_path = layer.data_path
207209
layer_data_chache[layer_name] = file_path
208210
return layer_data_chache[layer_name]
209211

@@ -243,7 +245,7 @@ def sample_coordinates_window(coords_dict, layer_models, bounds, round_val=None)
243245
244246
"""
245247
result_all = []
246-
path = layer_models[0].file_path
248+
path = layer_models[0].file_path if not layer_models[0].is_mbtile else layer_models[0].data_path
247249
coords = []
248250
for k, v in coords_dict.items():
249251
coords.extend(v)

server/guppy/endpoints/endpoints.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def get_stats_for_bbox(db: Session, layer_name: str, bbox_left: float, bbox_bott
4040
t = time.time()
4141
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_name).first()
4242
if layer_model:
43-
path = layer_model.file_path
43+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
4444
if os.path.exists(path) and bbox_left and bbox_bottom and bbox_right and bbox_top:
4545
with rasterio.open(path) as src:
4646
target_srs = src.crs.to_epsg()
@@ -74,7 +74,7 @@ def get_data_for_wkt(db: Session, layer_name: str, body: s.GeometryBody):
7474
t = time.time()
7575
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_name).first()
7676
if layer_model:
77-
path = layer_model.file_path
77+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
7878
if os.path.exists(path) and body:
7979
geom = wkt.loads(body.geometry)
8080
with rasterio.open(path) as src:
@@ -107,7 +107,7 @@ def get_stats_for_wkt(db: Session, layer_name: str, body: s.GeometryBody, native
107107
t = time.time()
108108
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_name).first()
109109
if layer_model:
110-
path = layer_model.file_path
110+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
111111
if os.path.exists(path) and body:
112112
geom = wkt.loads(body.geometry)
113113
with rasterio.open(path) as src:
@@ -150,7 +150,7 @@ def get_stats_for_model(layer_model, native, geom, srs):
150150
Returns:
151151
The statistics response if successful, otherwise None.
152152
"""
153-
path = layer_model.file_path
153+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
154154
if os.path.exists(path) and geom:
155155
with rasterio.open(path) as src:
156156
target_srs = src.crs.to_epsg()
@@ -205,7 +205,7 @@ def get_line_data_for_wkt(db: Session, layer_name: str, body: s.LineGeometryBody
205205
t = time.time()
206206
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_name).first()
207207
if layer_model:
208-
path = layer_model.file_path
208+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
209209
if os.path.exists(path) and body:
210210
line = wkt.loads(body.geometry)
211211
with rasterio.open(path) as src:
@@ -296,8 +296,9 @@ def get_multi_line_data_list_for_wkt(db: Session, body: s.MultiLineGeometryListB
296296
def get_point_value_from_layer(db: Session, layer_name: str, x: float, y: float):
297297
t = time.time()
298298
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_name).first()
299-
if layer_model and not layer_model.is_mbtile:
300-
path = layer_model.file_path
299+
is_raster = not layer_model.is_mbtile or layer_model.data_path != None
300+
if layer_model and is_raster:
301+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
301302
if os.path.exists(path) and x and y:
302303
with rasterio.open(path) as src:
303304
nodata = src.nodata
@@ -308,8 +309,8 @@ def get_point_value_from_layer(db: Session, layer_name: str, x: float, y: float)
308309
logger.info(f'get_point_value_from_raster 200 {time.time() - t}')
309310
return s.PointResponse(type='point value', layer_name=layer_name, value=None if math.isclose(float(v[0]), nodata) else float(v[0]))
310311
logger.warning(f'file not found {path}')
311-
elif layer_model and layer_model.is_mbtile:
312-
path = layer_model.file_path
312+
elif layer_model:
313+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
313314
if os.path.exists(path) and x and y:
314315
tile_z, tile_x, tile_y = latlon_to_tilexy(x, y, 14)
315316
tile = get_tile_data(layer_name=layer_name, mb_file=path, z=tile_z, x=tile_x, y=tile_y)
@@ -362,7 +363,8 @@ def get_line_object_list_for_wkt(db: Session, layer_name: str, body: s.LineObjec
362363
if layer_model.file_path.endswith('.pkl'):
363364
input_file_df = pd.read_pickle(layer_model.file_path)
364365
else:
365-
input_file_df = gpd.read_file(layer_model.file_path)
366+
367+
input_file_df = gpd.read_file(layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path)
366368
input_file_df.to_crs(crs='epsg:3857', inplace=True)
367369
input_file_df = input_file_df[~pd.isna(input_file_df.geometry)]
368370
input_file_df = input_file_df[~input_file_df.is_empty]
@@ -391,7 +393,7 @@ def get_classification_for_wkt(db: Session, layer_name: str, body: s.GeometryBod
391393
t = time.time()
392394
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_name).first()
393395
if layer_model:
394-
path = layer_model.file_path
396+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
395397
if os.path.exists(path) and body:
396398
geom = wkt.loads(body.geometry)
397399
with rasterio.open(path) as src:
@@ -433,7 +435,7 @@ def get_combine_layers(db: Session, body: s.CombineLayersGeometryBody):
433435
geom = wkt.loads(body.geometry)
434436
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_item.layer_name).first()
435437
if layer_model:
436-
path = layer_model.file_path
438+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
437439
if os.path.exists(path) and body:
438440
with rasterio.open(path) as src:
439441
target_srs = src.crs.to_epsg()
@@ -465,7 +467,8 @@ def get_combine_layers(db: Session, body: s.CombineLayersGeometryBody):
465467

466468

467469
def get_layer_contour(layer):
468-
with rasterio.open(layer.file_path) as src:
470+
file_path = layer.file_path if not layer.is_mbtile else layer.data_path
471+
with rasterio.open(file_path) as src:
469472
contour_geojson = list(dataset_features(src, bidx=1, as_mask=True, precision=1, band=False, geographic=False))
470473
return {'layerName': layer.layer_name, 'geometry': contour_geojson}
471474

@@ -489,7 +492,7 @@ def get_quantiles_for_wkt(db: Session, layer_name: str, body: s.QuantileBody, na
489492
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_name).first()
490493
if layer_model:
491494
quantiles = body.quantiles
492-
path = layer_model.file_path
495+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
493496
if os.path.exists(path) and body:
494497
geom = wkt.loads(body.geometry)
495498
with rasterio.open(path) as src:

server/guppy/endpoints/endpoints_admin.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ def delete_layer_mapping(db: Session, layer_name: str):
2727
if layer_model:
2828
if os.path.exists(layer_model.file_path):
2929
os.remove(layer_model.file_path)
30+
if os.path.exists(layer_model.data_path):
31+
os.remove(layer_model.data_path)
3032
db.delete(layer_model)
3133
db.commit()
3234
logger.info(f'delete_layer_mapping 200 {time.time() - t}')
@@ -35,7 +37,7 @@ def delete_layer_mapping(db: Session, layer_name: str):
3537
return Response(status_code=status.HTTP_204_NO_CONTENT)
3638

3739

38-
def update_layer_mapping(db: Session, layer_name: str, label: str, file_path: str, is_rgb: bool, is_mbtile: bool):
40+
def update_layer_mapping(db: Session, layer_name: str, label: str, file_path: str, data_path: str, is_rgb: bool, is_mbtile: bool):
3941
"""
4042
Updates the mapping of a layer in the database.
4143
@@ -55,6 +57,7 @@ def update_layer_mapping(db: Session, layer_name: str, label: str, file_path: st
5557
if layer_model:
5658
layer_model.label = label
5759
layer_model.file_path = file_path
60+
layer_model.data_path = data_path
5861
layer_model.is_rgb = is_rgb
5962
layer_model.is_mbtile = is_mbtile
6063
db.commit()
@@ -64,10 +67,9 @@ def update_layer_mapping(db: Session, layer_name: str, label: str, file_path: st
6467
return Response(status_code=status.HTTP_204_NO_CONTENT)
6568

6669

67-
def insert_layer_mapping(db: Session, layer_name: str, label: str, file_path: str, is_rgb: bool, is_mbtile: bool):
70+
def insert_layer_mapping(db: Session, layer_name: str, label: str, file_path: str, data_path: str, is_rgb: bool, is_mbtile: bool):
6871
"""
6972
Inserts a layer mapping into the database.
70-
7173
Args:
7274
db: The session object for the database connection.
7375
layer_name: The name of the layer.
@@ -80,7 +82,7 @@ def insert_layer_mapping(db: Session, layer_name: str, label: str, file_path: st
8082
8183
"""
8284
t = time.time()
83-
layer_model = m.LayerMetadata(layer_name=layer_name, label=label, file_path=file_path, is_rgb=is_rgb, is_mbtile=is_mbtile)
85+
layer_model = m.LayerMetadata(layer_name=layer_name, label=label, file_path=file_path, data_path=data_path, is_rgb=is_rgb, is_mbtile=is_mbtile)
8486
db.add(layer_model)
8587
db.commit()
8688
logger.info(f'insert_layer_mapping 201 {time.time() - t}')

server/guppy/endpoints/endpoints_upload.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,23 @@
1515
logger = logging.getLogger(__name__)
1616

1717

18-
def upload_file(layer_name: str, label: str, file: UploadFile, db: Session, is_rgb: bool = False, max_zoom: int = 17):
18+
def upload_file(layer_name: str, label: str, file: UploadFile, data: UploadFile | None, db: Session, is_rgb: bool = False, max_zoom: int = 17):
1919
"""
2020
Args:
2121
layer_name (str): The name of the layer.
2222
label (str): The label of the layer.
2323
file (UploadFile): The file to upload.
24+
data (UploadFile): The data to upload (if the input is a mbtile raster, we need the data geotiff aswell for sampling functions).
2425
db (Session): The database session.
2526
is_rgb (bool, optional): Indicates whether the file is in RGB format.
2627
2728
"""
28-
29+
data_location = None
2930
filename_without_extension, ext = os.path.splitext(file.filename)
31+
if data:
32+
dataname_without_extension, d_ext = os.path.splitext(data.filename)
33+
sanitized_dataname = sanitize_input_str(filename_without_extension)
34+
validate_file_input(d_ext, data, dataname_without_extension, layer_name)
3035

3136
validate_file_input(ext, file, filename_without_extension, layer_name)
3237

@@ -35,13 +40,16 @@ def upload_file(layer_name: str, label: str, file: UploadFile, db: Session, is_r
3540

3641
check_layer_exists(layer_name=f"{sanitized_layer_name}", db=db)
3742

38-
file_location, tmp_file_location = create_location_paths_and_check_if_exists(ext, sanitized_filename, sanitized_layer_name)
39-
43+
file_location, tmp_file_location = create_location_paths_and_check_if_exists(ext, sanitized_filename, sanitized_layer_name, is_raster=True if data else False)
4044
write_input_file_to_disk(file, tmp_file_location)
4145

46+
if data:
47+
data_location, tmp_data_location = create_location_paths_and_check_if_exists(d_ext, sanitized_dataname, sanitized_layer_name, is_raster=True)
48+
write_input_file_to_disk(data, tmp_data_location)
49+
4250
is_mbtile = create_preprocessed_layer_file(ext, file_location, sanitized_filename, sanitized_layer_name, tmp_file_location, max_zoom)
4351

44-
insert_into_layer_metadata(layer_uuid=sanitized_layer_name, label=label, file_path=file_location, db=db, is_rgb=is_rgb, is_mbtile=is_mbtile)
52+
insert_into_layer_metadata(layer_uuid=sanitized_layer_name, label=label, file_path=file_location, data_path=data_location, db=db, is_rgb=is_rgb, is_mbtile=is_mbtile)
4553
return f"Upload successful: Layer {sanitized_layer_name} uploaded with label {label}."
4654

4755

server/guppy/endpoints/raster_calc_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ def fill_path_and_argument_lists(arguments_list, layer_list, db, nodata, path_li
465465
for layer_item in layer_list:
466466
layer_model = db.query(m.LayerMetadata).filter_by(layer_name=layer_item.layer_name).first()
467467
if layer_model:
468-
path = layer_model.file_path
468+
path = layer_model.file_path if not layer_model.is_mbtile else layer_model.data_path
469469
path_list.append(path)
470470
if os.path.exists(path):
471471
with rasterio.open(path) as src:

server/guppy/endpoints/upload_utils.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,15 @@ def create_preprocessed_layer_file(ext: str, file_location: str, sanitized_filen
157157
return is_mbtile
158158

159159

160-
def create_location_paths_and_check_if_exists(ext: str, sanitized_filename: str, sanitized_layer_name: str) -> tuple[str, str]:
160+
def create_location_paths_and_check_if_exists(ext: str, sanitized_filename: str, sanitized_layer_name: str, is_raster=False) -> tuple[str, str]:
161161
"""
162162
Creates file paths for the uploaded file and checks if the file already exists.
163163
164164
Args:
165165
ext (str): The file extension.
166166
sanitized_filename (str): The sanitized name of the file.
167167
sanitized_layer_name (str): The sanitized name of the layer.
168+
is_raster (bool): Optional. Indicates whether the file is a raster file. Default is False.
168169
169170
Returns:
170171
tuple: A tuple containing the file location and temporary file location.
@@ -179,7 +180,11 @@ def create_location_paths_and_check_if_exists(ext: str, sanitized_filename: str,
179180
if os.path.exists(file_location):
180181
raise create_error(message=f"Upload failed: File {sanitized_layer_name}_{sanitized_filename}.tif already exists.", code=400)
181182
elif ext.lower() in ['.mbtiles']:
182-
file_location = f"{cfg.deploy.content}/shapefiles/uploaded/{sanitized_layer_name}_{sanitized_filename}.mbtiles"
183+
if is_raster:
184+
folder = 'tifs'
185+
else:
186+
folder = 'shapefiles'
187+
file_location = f"{cfg.deploy.content}/{folder}/uploaded/{sanitized_layer_name}_{sanitized_filename}.mbtiles"
183188
tmp_file_location = file_location
184189
if os.path.exists(file_location):
185190
raise create_error(message=f"Upload failed: File {sanitized_layer_name}_{sanitized_filename}.mbtiles already exists.", code=400)
@@ -309,7 +314,7 @@ def save_geotif_tiled_overviews(input_file: str, output_file: str, nodata: int)
309314
return output_file
310315

311316

312-
def insert_into_layer_metadata(layer_uuid: str, label: str, file_path: str, db: Session, is_rgb: bool = False, is_mbtile: bool = False):
317+
def insert_into_layer_metadata(layer_uuid: str, label: str, file_path: str, data_path: str, db: Session, is_rgb: bool = False, is_mbtile: bool = False):
313318
"""
314319
Inserts a record into the layer_metadata table.
315320
@@ -319,7 +324,7 @@ def insert_into_layer_metadata(layer_uuid: str, label: str, file_path: str, db:
319324
db: The database connection object.
320325
is_rgb: Optional. Indicates whether the layer is an RGB layer. Default is False.
321326
"""
322-
new_layer = LayerMetadata(layer_name=layer_uuid, label=label, file_path=file_path, is_rgb=is_rgb, is_mbtile=is_mbtile)
327+
new_layer = LayerMetadata(layer_name=layer_uuid, label=label, file_path=file_path, data_path=data_path, is_rgb=is_rgb, is_mbtile=is_mbtile)
323328
db.add(new_layer)
324329
db.commit()
325330
logger.info("Record inserted into layer metadata")

server/guppy/html/layers.html

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ <h1>Layer Metadata</h1>
9191
<input type="text" id="layer_name" name="layer_name" placeholder="Layer Name" required>
9292
<input type="text" id="label" name="label" placeholder="Layer Label" required>
9393
<input type="text" id="file_path" name="file_path" placeholder="File Path" required>
94+
<input type="text" id="data_path" name="data_path" placeholder="Data Path" required>
9495
<label>
9596
<input type="checkbox" id="is_rgb" name="is_rgb"> Is RGB
9697
</label>
@@ -110,6 +111,7 @@ <h1>Layer Metadata</h1>
110111
const layerName = document.getElementById('layer_name').value;
111112
const label = document.getElementById('label').value;
112113
const filePath = document.getElementById('file_path').value;
114+
const dataPath = document.getElementById('data_path').value;
113115
const isRgb = document.getElementById('is_rgb').checked;
114116
const isMbtile = document.getElementById('is_mbtile').checked;
115117

@@ -118,7 +120,7 @@ <h1>Layer Metadata</h1>
118120
headers: {
119121
'Content-Type': 'application/json',
120122
},
121-
body: JSON.stringify({layerName, label, filePath, isRgb, isMbtile}),
123+
body: JSON.stringify({layerName, label, filePath,dataPath, isRgb, isMbtile}),
122124
});
123125

124126
if (response.ok) {
@@ -144,6 +146,7 @@ <h1>Layer Metadata</h1>
144146
<th>Name</th>
145147
<th>Label</th>
146148
<th>Path</th>
149+
<th>Data Path</th>
147150
<th>Is RGB</th>
148151
<th>Is MBTile</th>
149152
<th>Actions</th>
@@ -159,15 +162,17 @@ <h1>Layer Metadata</h1>
159162
const nameCell = row.insertCell(0);
160163
const labelCell = row.insertCell(1);
161164
const pathCell = row.insertCell(2);
162-
const rgbCell = row.insertCell(3);
163-
const mbtileCell = row.insertCell(4);
164-
const deleteCell = row.insertCell(5); // Cell for the delete button
165-
const statsCell = row.insertCell(6); // Cell for the stats button
166-
const downloadCell = row.insertCell(7); // Cell for the download button
165+
const dataCell = row.insertCell(3);
166+
const rgbCell = row.insertCell(4);
167+
const mbtileCell = row.insertCell(5);
168+
const deleteCell = row.insertCell(6); // Cell for the delete button
169+
const statsCell = row.insertCell(7); // Cell for the stats button
170+
const downloadCell = row.insertCell(8); // Cell for the download button
167171

168172
nameCell.textContent = layer.layerName;
169173
labelCell.textContent = layer.effectiveLabel;
170174
pathCell.textContent = layer.filePath;
175+
dataCell.textContent = layer.dataPath;
171176
rgbCell.textContent = layer.isRgb ? 'Yes' : 'No';
172177
mbtileCell.textContent = layer.isMbtile ? 'Yes' : 'No';
173178

0 commit comments

Comments
 (0)