Skip to content

Commit

Permalink
Merge pull request #259 from hotosm/feature/custom-tiles
Browse files Browse the repository at this point in the history
Feature : Vector tile support for custom exports
  • Loading branch information
kshitijrajsharma authored Jul 30, 2024
2 parents 27bc855 + 45dd7dd commit 1d74039
Show file tree
Hide file tree
Showing 5 changed files with 1,373 additions and 1,364 deletions.
5 changes: 5 additions & 0 deletions API/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

# Standard library imports
# Humanitarian OpenStreetmap Team
# 1100 13th Street NW Suite 800 Washington, D.C. 20005
# <[email protected]>
import time

# Third party imports
import psycopg2
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
Expand All @@ -26,6 +28,7 @@
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded

# Reader imports
from src.config import (
ENABLE_CUSTOM_EXPORTS,
ENABLE_HDX_EXPORTS,
Expand Down Expand Up @@ -57,6 +60,7 @@
from .hdx import router as hdx_router

if SENTRY_DSN:
# Third party imports
import sentry_sdk

# only use sentry if it is specified in config blocks
Expand All @@ -71,6 +75,7 @@

if LOG_LEVEL.lower() == "debug":
# This is used for local setup for auth login
# Standard library imports
import os

os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
Expand Down
58 changes: 27 additions & 31 deletions src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,27 @@ def ogr_export(query, outputtype, working_dir, dump_temp_path, params):
params.min_zoom, params.max_zoom
)
if params.min_zoom and params.max_zoom
else "-dsco ZOOM_LEVEL_AUTO=YES"
else "MINZOOM=0 MAXZOOM=20"
),
}
format_options[RawDataOutputType.PMTILES.value] = {
"format": "PMTiles",
"extra": (
"-dsco MINZOOM={} -dsco MAXZOOM={} ".format(
params.min_zoom, params.max_zoom
)
if params.min_zoom and params.max_zoom
else "-dsco MINZOOM=0 MAXZOOM=20"
),
}
format_options[RawDataOutputType.MVT.value] = {
"format": "MVT",
"extra": (
"-dsco MINZOOM={} -dsco MAXZOOM={} ".format(
params.min_zoom, params.max_zoom
)
if params.min_zoom and params.max_zoom
else "-dsco MINZOOM=0 MAXZOOM=20 FORMAT=MBTILES"
),
}

Expand Down Expand Up @@ -689,16 +709,6 @@ def get_grid_id(geom, cur):
country_export,
)

@staticmethod
def geojson2tiles(geojson_path, tile_path, tile_layer_name):
"""Responsible for geojson to tiles"""
cmd = """tippecanoe -zg --projection=EPSG:4326 -o {tile_output_path} -l {tile_layer_name} --force {geojson_input_path}""".format(
tile_output_path=tile_path,
tile_layer_name=tile_layer_name,
geojson_input_path=geojson_path,
)
run_ogr2ogr_cmd(cmd)

def extract_current_data(self, exportname):
"""Responsible for Extracting rawdata current snapshot, Initially it creates a geojson file , Generates query , run it with 1000 chunk size and writes it directly to the geojson file and closes the file after dump
Args:
Expand Down Expand Up @@ -731,25 +741,11 @@ def extract_current_data(self, exportname):
try:
# currently we have only geojson binding function written other than that we have depend on ogr
if ENABLE_TILES:
if output_type == RawDataOutputType.PMTILES.value:
geojson_path = os.path.join(
working_dir,
f"{self.params.file_name if self.params.file_name else 'Export'}.geojson",
)
RawData.query2geojson(
self.con,
raw_currentdata_extraction_query(
self.params,
g_id=grid_id,
c_id=country,
country_export=country_export,
),
geojson_path,
)
RawData.geojson2tiles(
geojson_path, dump_temp_file_path, self.params.file_name
)
if output_type == RawDataOutputType.MBTILES.value:
if output_type in [
RawDataOutputType.PMTILES.value,
RawDataOutputType.MBTILES.value,
RawDataOutputType.MVT.value,
]:
RawData.ogr_export(
query=raw_currentdata_extraction_query(
self.params,
Expand All @@ -762,7 +758,7 @@ def extract_current_data(self, exportname):
dump_temp_path=dump_temp_file_path,
working_dir=working_dir,
params=self.params,
) # uses ogr export to export
)

if output_type == RawDataOutputType.GEOJSON.value:
RawData.query2geojson(
Expand Down
33 changes: 20 additions & 13 deletions src/query_builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,18 +140,22 @@ def create_column_filter(
schema = {}
schema["osm_id"] = "int64"
schema["type"] = "str"

for cl in columns:
splitted_cl = [cl]
if "," in cl:
splitted_cl = cl.split(",")
for cl in splitted_cl:
if cl != "":
filter_col.append(
f"""tags ->> '{cl.strip()}' as {remove_spaces(cl.strip())}"""
)
if create_schema:
schema[remove_spaces(cl.strip())] = "str"
if "*" in columns:
filter_col.append("tags")
if create_schema:
schema["tags"] = "str"
else:
for cl in columns:
splitted_cl = [cl]
if "," in cl:
splitted_cl = cl.split(",")
for cl in splitted_cl:
if cl != "":
filter_col.append(
f"""tags ->> '{cl.strip()}' as {remove_spaces(cl.strip())}"""
)
if create_schema:
schema[remove_spaces(cl.strip())] = "str"
if output_type == "csv": # if it is csv geom logic is different
filter_col.append("ST_X(ST_Centroid(geom)) as longitude")
filter_col.append("ST_Y(ST_Centroid(geom)) as latitude")
Expand Down Expand Up @@ -973,7 +977,10 @@ def extract_features_custom_exports(
},
}
if USE_DUCK_DB_FOR_CUSTOM_EXPORTS is True:
select = [f"""tags['{item}'][1] as "{item}" """ for item in select]
if "*" in select:
select = [f"""tags::json as tags """]
else:
select = [f"""tags['{item}'][1] as "{item}" """ for item in select]
select += ["osm_id", "osm_type", "geom"]
select_query = ", ".join(select)
else:
Expand Down
67 changes: 34 additions & 33 deletions src/validation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class RawDataOutputType(Enum):
if ENABLE_TILES:
MBTILES = "mbtiles"
PMTILES = "pmtiles" ## EXPERIMENTAL
MVT = "mvt" ## Experimental


class SupportedFilters(Enum):
Expand Down Expand Up @@ -297,22 +298,22 @@ class StatsRequestParams(BaseModel, GeometryValidatorMixin):
max_length=3,
example="NPL",
)
geometry: Optional[
Union[Polygon, MultiPolygon, Feature, FeatureCollection]
] = Field(
default=None,
example={
"type": "Polygon",
"coordinates": [
[
[83.96919250488281, 28.194446860487773],
[83.99751663208006, 28.194446860487773],
[83.99751663208006, 28.214869548073377],
[83.96919250488281, 28.214869548073377],
[83.96919250488281, 28.194446860487773],
]
],
},
geometry: Optional[Union[Polygon, MultiPolygon, Feature, FeatureCollection]] = (
Field(
default=None,
example={
"type": "Polygon",
"coordinates": [
[
[83.96919250488281, 28.194446860487773],
[83.99751663208006, 28.194446860487773],
[83.99751663208006, 28.214869548073377],
[83.96919250488281, 28.214869548073377],
[83.96919250488281, 28.194446860487773],
]
],
},
)
)

@validator("geometry", pre=True, always=True)
Expand Down Expand Up @@ -479,7 +480,7 @@ def __init__(self, suffix, driver_name, layer_creation_options, format_option):
"gpkg": ExportTypeInfo("gpkg", "GPKG", ["SPATIAL_INDEX=No"], "GDAL"),
"sqlite": ExportTypeInfo("sqlite", "SQLite", [], "GDAL"),
"fgb": ExportTypeInfo("fgb", "FlatGeobuf", ["VERIFY_BUFFERS=NO"], "GDAL"),
"mvt": ExportTypeInfo("mvt", "MVT", [], "GDAL"),
"mvt": ExportTypeInfo("mbtiles", "MVT", ["MAXZOOM=20"], "GDAL"),
"kml": ExportTypeInfo("kml", "KML", [], "GDAL"),
"gpx": ExportTypeInfo("gpx", "GPX", [], "GDAL"),
"parquet": ExportTypeInfo("parquet", "PARQUET", [], "PARQUET"),
Expand Down Expand Up @@ -618,22 +619,22 @@ class DynamicCategoriesModel(CategoriesBase, GeometryValidatorMixin):
max_length=3,
example="USA",
)
geometry: Optional[
Union[Polygon, MultiPolygon, Feature, FeatureCollection]
] = Field(
default=None,
example={
"type": "Polygon",
"coordinates": [
[
[83.96919250488281, 28.194446860487773],
[83.99751663208006, 28.194446860487773],
[83.99751663208006, 28.214869548073377],
[83.96919250488281, 28.214869548073377],
[83.96919250488281, 28.194446860487773],
]
],
},
geometry: Optional[Union[Polygon, MultiPolygon, Feature, FeatureCollection]] = (
Field(
default=None,
example={
"type": "Polygon",
"coordinates": [
[
[83.96919250488281, 28.194446860487773],
[83.99751663208006, 28.194446860487773],
[83.99751663208006, 28.214869548073377],
[83.96919250488281, 28.214869548073377],
[83.96919250488281, 28.194446860487773],
]
],
},
)
)

@validator("geometry", pre=True, always=True)
Expand Down
Loading

0 comments on commit 1d74039

Please sign in to comment.