diff --git a/.vscode/launch.json b/.vscode/launch.json index 8836237..21384ba 100755 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,30 +2,17 @@ "version": "0.2.0", "configurations": [ { - "name": "Kart: Command line", - "type": "python", - "request": "launch", - "program": "${workspaceFolder}/src/collection/kart/prepare_kart.py", - "console": "integratedTerminal", - "args": [ - "--repo_url", "https://github.com/goat-community/pois", - "--maintainer", "rds", - "--table_name", "poi" - ], - "cwd": "${workspaceFolder}" - }, - { - "name": "CLI: Command line", - "type": "python", + "name": "Data Preparation CLI: manage.py", + "type": "debugpy", "request": "launch", "program": "${workspaceFolder}/manage.py", "console": "integratedTerminal", "args": [ - "--actions", "preparation", - "--region", "de", - "--datasets", "poi_osm_overture_fusion" + "--actions", "", + "--datasets", "", + "--region", "", ], "cwd": "${workspaceFolder}" } ] -} \ No newline at end of file +} diff --git a/Dockerfile b/Dockerfile index ded6763..4f68078 100755 --- a/Dockerfile +++ b/Dockerfile @@ -26,9 +26,9 @@ RUN apt update RUN apt install -y s3fs # Install kart -RUN apt-get install -y libtinfo5 -RUN wget "https://goat-db-schemas.s3.eu-central-1.amazonaws.com/kart.deb" -RUN dpkg -i kart.deb +# RUN apt-get install -y libtinfo5 +# RUN wget "https://goat-db-schemas.s3.eu-central-1.amazonaws.com/kart.deb" +# RUN dpkg -i kart.deb # install posgresql-client RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' @@ -40,6 +40,9 @@ RUN apt-get install -y postgresql-client-15 RUN apt install -y nano RUN git config --global core.editor "nano" +# Install zip +RUN apt install -y zip + # Install Java 11 RUN echo 'deb http://deb.debian.org/debian bullseye main' > /etc/apt/sources.list RUN apt-get update && \ diff --git a/README.md b/README.md index 4cadf53..75333c7 100755 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Preparation - poi_overture - network - network_pt -- network_overture +- overture_street_network - gtfs - gtfs_stops - gtfs_stations diff --git a/manage.py b/manage.py index 615df47..d4cc2ca 100644 --- a/manage.py +++ b/manage.py @@ -6,23 +6,24 @@ from src.collection.gtfs import collect_gtfs from src.collection.landuse import collect_landuse from src.collection.network import collect_network -from src.collection.network_pt import collect_network_pt from src.collection.osm_pt_lines import collect_osm_pt_lines from src.collection.overture import collect_overture from src.collection.poi import collect_poi from src.core.config import settings from src.db.db import Database +from src.export.gtfs import export_gtfs from src.fusion.poi_osm_overture import fusion_poi_osm_overture from src.migration.gtfs import migrate_gtfs from src.preparation.building import prepare_building -from src.preparation.gtfs import export_gtfs, prepare_gtfs +from src.preparation.gtfs import prepare_gtfs from src.preparation.gtfs_stations import prepare_gtfs_stations from src.preparation.gtfs_stops import prepare_gtfs_stops from src.preparation.network import export_network, prepare_network -from src.preparation.network_overture import prepare_overture_network from src.preparation.network_pt import prepare_network_pt from src.preparation.osm_pt_lines import prepare_osm_pt_lines from src.preparation.overture_division_area import prepare_overture_division_area +from src.preparation.overture_place import prepare_overture_place +from src.preparation.overture_street_network import prepare_overture_street_network from src.preparation.poi import export_poi, prepare_poi from src.preparation.poi_overture import prepare_poi_overture from src.preparation.population import prepare_population @@ -40,7 +41,6 @@ "poi": collect_poi, "landuse": collect_landuse, "network": collect_network, - "network_pt": collect_network_pt, "gtfs": collect_gtfs, "overture": collect_overture, "osm_pt_lines": collect_osm_pt_lines, @@ -53,11 +53,12 @@ "building": prepare_building, "population": prepare_population, "gtfs": prepare_gtfs, - "network_overture": prepare_overture_network, + "overture_street_network": prepare_overture_street_network, "overture": prepare_overture_division_area, "gtfs_stops": prepare_gtfs_stops, "gtfs_stations": prepare_gtfs_stations, "osm_pt_lines": prepare_osm_pt_lines, + "overture_place": prepare_overture_place, }, "fusion":{ "poi_osm_overture": fusion_poi_osm_overture, @@ -65,7 +66,7 @@ "export": { "poi": export_poi, "network": export_network, - "gtfs": export_gtfs + "gtfs": export_gtfs, }, "migration": { "gtfs": migrate_gtfs diff --git a/src/collection/gtfs.py b/src/collection/gtfs.py index 73a5bb7..adf068c 100644 --- a/src/collection/gtfs.py +++ b/src/collection/gtfs.py @@ -180,7 +180,7 @@ def import_file(self, input_dir: str, table: str, header: list, table_columns: l sql_copy = f""" INSERT INTO {self.schema}.{table} ({output_cols_formatted}, geom, h3_3) SELECT {output_cols_formatted}, ST_SetSRID(ST_MakePoint(shape_pt_lon, shape_pt_lat), 4326) AS geom, - public.to_short_h3_3(h3_lat_lng_to_cell(ST_SetSRID(ST_MakePoint(shape_pt_lon, shape_pt_lat), 4326)::point, 3)::bigint) AS h3_3 + basic.to_short_h3_3(h3_lat_lng_to_cell(ST_SetSRID(ST_MakePoint(shape_pt_lon, shape_pt_lat), 4326)::point, 3)::bigint) AS h3_3 FROM {self.schema}.{table}_temp; """ elif table == "stops": @@ -196,7 +196,7 @@ def import_file(self, input_dir: str, table: str, header: list, table_columns: l ); UPDATE {self.schema}.{table}_temp - SET h3_3 = to_short_h3_3( + SET h3_3 = basic.to_short_h3_3( h3_lat_lng_to_cell(ST_SetSRID(ST_MakePoint(stop_lon, stop_lat), 4326)::point, 3)::bigint ) WHERE h3_3 IS NULL; diff --git a/src/collection/network_pt.py b/src/collection/network_pt.py deleted file mode 100644 index b6446ad..0000000 --- a/src/collection/network_pt.py +++ /dev/null @@ -1,121 +0,0 @@ -import os -import subprocess -from pathlib import Path - -from src.config.config import Config -from src.core.config import settings -from src.db.db import Database -from src.utils.utils import ( - delete_file, - download_link, - make_dir, - osm_crop_to_polygon, - osm_generate_polygon, -) - - -class NetworkPTCollection(): - """Collects GTFS network and OSM data for a specified region and its sub-regions""" - - def __init__(self, db_rd, config, region): - self.db_rd = db_rd - self.region = region - self.region_osm_url = config.get("region_pbf") - self.s3_sub_region_osm_dir = config.get("s3_sub_region_osm_dir") - self.s3_sub_region_gtfs_dir = config.get("s3_sub_region_gtfs_dir") - self.sub_regions = self.db_rd.select(config.get("sub_regions_query")) - - self.region_osm_filename = os.path.basename(self.region_osm_url) - self.region_osm_input_dir = os.path.join(settings.INPUT_DATA_DIR, "network_pt", region) - self.sub_region_gtfs_input_dir = os.path.join(settings.INPUT_DATA_DIR, "network_pt", region) - self.sub_region_osm_output_dir = os.path.join(settings.OUTPUT_DATA_DIR, "network_pt", region) - - - def collect_osm(self): - """Downloads the latest OSM data for this region""" - - print(f"Downloading OSM data for region: {self.region}") - make_dir(dir_path=self.region_osm_input_dir) - download_link( - directory=self.region_osm_input_dir, - link=self.region_osm_url - ) - - - def collect_gtfs(self): - """Downloads GTFS networks for all sub-regions within this region""" - - for id in self.sub_regions: - id = int(id[0]) - print(f"Downloading GTFS network for region: {self.region}, sub-region: {id}") - make_dir(dir_path=self.sub_region_gtfs_input_dir) - settings.S3_CLIENT.download_file( - settings.AWS_BUCKET_NAME, - f"{self.s3_sub_region_gtfs_dir}/{id}.zip", - os.path.join(self.sub_region_gtfs_input_dir, f"{id}.zip") - ) - - - def process_osm(self): - """Crops OSM data for all sub-regions within this region""" - - # Generate sub-region polygon filters - print(f"Generating sub-region filters for region: {self.region}") - make_dir(dir_path=self.sub_region_osm_output_dir) - for id in self.sub_regions: - id = int(id[0]) - osm_generate_polygon( - db_rd=self.db_rd, - geom_query=f"SELECT buffer_geom as geom FROM public.gtfs_regions WHERE id = {id}", - dest_file_path=os.path.join(self.sub_region_osm_output_dir, f"{id}.poly") - ) - - # Crop region OSM data as per sub-region polygon filters - for id in self.sub_regions: - id = int(id[0]) - print(f"Cropping OSM data for region: {self.region}, sub-region: {id}") - osm_crop_to_polygon( - orig_file_path=os.path.join(self.region_osm_input_dir, self.region_osm_filename), - dest_file_path=os.path.join(self.sub_region_osm_output_dir, f"{id}.pbf"), - poly_file_path=os.path.join(self.sub_region_osm_output_dir, f"{id}.poly") - ) - delete_file(file_path=os.path.join(self.sub_region_osm_output_dir, f"{id}.poly")) - - - def upload_osm(self): - """Uploads cropped OSM sub-region data to S3""" - - for id in self.sub_regions: - id = int(id[0]) - print(f"Uploading cropped OSM data for region: {self.region}, sub-region: {id}") - settings.S3_CLIENT.upload_file( - os.path.join(self.sub_region_osm_output_dir, f"{id}.pbf"), - settings.AWS_BUCKET_NAME, - f"{self.s3_sub_region_osm_dir}/{id}.pbf" - ) - - -def collect_network_pt(region: str): - """Main function.""" - - db_rd = Database(settings.RAW_DATABASE_URI) - try: - config = Config(name="network_pt", region=region) - network_pt_collection = NetworkPTCollection( - db_rd=db_rd, - config=config.config, - region=region - ) - network_pt_collection.collect_osm() - network_pt_collection.collect_gtfs() - network_pt_collection.process_osm() - network_pt_collection.upload_osm() - except Exception as e: - print(e) - raise e - finally: - db_rd.conn.close() - - -if __name__ == "__main__": - collect_network_pt() diff --git a/src/collection/overture.py b/src/collection/overture.py index c6959bd..b774d58 100644 --- a/src/collection/overture.py +++ b/src/collection/overture.py @@ -1,4 +1,5 @@ import pyspark.sql.types as pyspark_types +from pyspark.sql import SparkSession from pyspark.sql.functions import expr, to_json from sedona.spark import SedonaContext @@ -48,8 +49,8 @@ def validate_config(self): if not self.data_config_collection.get("local_result_table"): raise ValueError("Local result table not specified.") - def initialize_sedona_context(self): - """Initialze Sedona context with required dependencies, AWS credentials provider and resource allocations.""" + def initialize_spark_session(self) -> SparkSession: + """Initialze SparkSession with required dependencies, AWS credentials provider and resource allocations.""" config = SedonaContext.builder() \ .config('spark.jars.packages', @@ -86,11 +87,11 @@ def build_overture_s3_uri(self, version, theme, type): return f"s3a://overturemaps-us-west-2/release/{version}/theme={theme}/type={type}" - def initialize_data_source(self, sedona: SedonaContext): + def initialize_data_source(self, spark: SparkSession): """Initialize Overture geoparquet file source and Spark data frames.""" # Load Overture geoparquet data into Spark DataFrames - self.data_frame = sedona.read.format("geoparquet").load( + self.data_frame = spark.read.format("geoparquet").load( path=self.build_overture_s3_uri( version=self.data_config_collection["version"], theme=self.data_config_collection["theme"], @@ -174,9 +175,9 @@ def run(self): self.validate_config() # Initialize Overture data source - sedona = self.initialize_sedona_context() + spark = self.initialize_spark_session() self.initialize_jdbc_properties() - self.initialize_data_source(sedona) + self.initialize_data_source(spark) # Process data frame and filter by region bounds bbox_coords = get_region_bbox_coords( diff --git a/src/collection/poi.py b/src/collection/poi.py index f8f4059..110b2dc 100755 --- a/src/collection/poi.py +++ b/src/collection/poi.py @@ -24,7 +24,7 @@ def poi_collection(self): osm_filter += tag + " " if osm_filter: - '--keep="' + osm_filter + '"' + osm_filter = '--keep="' + osm_filter + '"' # Remove not needed osm feature categories if self.data_config.collection["nodes"] == False: diff --git a/src/config/config.py b/src/config/config.py index 44304d1..3d3b9c8 100755 --- a/src/config/config.py +++ b/src/config/config.py @@ -1,4 +1,5 @@ import os +import subprocess import yaml @@ -6,8 +7,6 @@ from src.core.config import settings from src.utils.utils import download_link, print_info -import subprocess - class Config: """Reads the config file and returns the config variables. @@ -28,6 +27,7 @@ def __init__(self, name: str, region: str): self.name = name self.collection = self.config.get("collection") self.preparation = self.config.get("preparation") + self.export = self.config.get("export") self.subscription = self.config.get("subscription") self.analysis = self.config.get("analysis") self.pbf_data = self.config.get("region_pbf") diff --git a/src/config/data_variables/gtfs/gtfs_de.yaml b/src/config/data_variables/gtfs/gtfs_de.yaml index aa04f64..f27c0ab 100644 --- a/src/config/data_variables/gtfs/gtfs_de.yaml +++ b/src/config/data_variables/gtfs/gtfs_de.yaml @@ -1,6 +1,9 @@ preparation: start_date: "2025-02-04" # Must be a Tuesday for stop_times_optimized to be correct! num_weeks: 26 - network_dir: "de" - target_schema: "gtfs_de" + network_dir: "gtfs_de_20250203" + target_schema: "gtfs_de_20250203" regions_query: "SELECT nuts_id, nuts_name FROM public.nuts WHERE cntr_code IN ('DE') AND levl_code = '3';" + +export: + local_gtfs_schema: "gtfs_de_20250106" diff --git a/src/config/data_variables/network_pt/network_pt_de.yaml b/src/config/data_variables/network_pt/network_pt_de.yaml new file mode 100644 index 0000000..ecb3faa --- /dev/null +++ b/src/config/data_variables/network_pt/network_pt_de.yaml @@ -0,0 +1,28 @@ +preparation: + # A SQL query which returns the region's bounding geometry (omit terminating semicolon) + region: "SELECT ST_Union(geom) AS geom FROM public.germany_states" + + # File in the src/data/input/network_pt directory where OSM data is stored + local_osm_file: "germany-latest.osm.pbf" + + # Schema in local database where GTFS data was collected + local_gtfs_schema: "gtfs_de_20250203" + + # Table in local database where sub-regions will be written + local_sub_region_table: "temporal.sub_region_de" + + # Weekday dates that will be used to optimize the GTFS data + # Leave empty if no optimization is desired + weekday_tuesday: "2025-02-18" + weekday_saturday: "2025-02-22" + weekday_sunday: "2025-02-23" + + # Buffer distance in meters to add to the sub-region's geometry + sub_region_buffer_dist: 80000 + + # Number of sub-regions to divide the region into + sub_region_count: 4 + +export: + # Configure whether the script should delete old regions and bundles from R5 automatically + delete_old_regions: false diff --git a/src/config/data_variables/network_pt/network_pt_eu.yaml b/src/config/data_variables/network_pt/network_pt_eu.yaml deleted file mode 100644 index b1f65bf..0000000 --- a/src/config/data_variables/network_pt/network_pt_eu.yaml +++ /dev/null @@ -1,4 +0,0 @@ -region_pbf: "https://download.geofabrik.de/europe-latest.osm.pbf" -sub_regions_query: "SELECT id FROM public.gtfs_regions ORDER BY id" -s3_sub_region_osm_dir: "network-pt/osm-regions/eu" -s3_sub_region_gtfs_dir: "network-pt/gtfs-regions/eu" diff --git a/src/config/data_variables/overture/overture_connector_europe.yaml b/src/config/data_variables/overture/overture_connector_europe.yaml index c1b3fb4..69500ba 100644 --- a/src/config/data_variables/overture/overture_connector_europe.yaml +++ b/src/config/data_variables/overture/overture_connector_europe.yaml @@ -1,6 +1,6 @@ collection: # Overture release version - version: 2024-09-18.0 + version: 2025-01-22.0 # Overture data theme theme: transportation @@ -9,19 +9,8 @@ collection: type: connector # Table in local database where data will be written - local_result_table: temporal.connector_europe + local_result_table: temporal.connectors # A SQL query which returns the region's bounding geometry (omit terminating semicolon) # Leave this empty if global data is to be fetched region: "SELECT ST_Union(geom) AS geom FROM poi.geom_ref WHERE id = 'geofence_active_mobility'" - -preparation: - # Name of text file containing a list of URLs to download DEM files - dem_source_list: dem_source_list_eu.txt - - # Coefficients specifying the cost of cycling on various surfaces - cycling_surfaces: - pavingStones: '0.2' - unpaved: '0.2' - gravel: '0.3' - dirt: '0.4' diff --git a/src/config/data_variables/overture/overture_place_europe.yaml b/src/config/data_variables/overture/overture_place_europe.yaml index 54f2a1e..6ce082c 100644 --- a/src/config/data_variables/overture/overture_place_europe.yaml +++ b/src/config/data_variables/overture/overture_place_europe.yaml @@ -1,6 +1,6 @@ collection: # Overture release version - version: 2024-09-18.0 + version: 2025-02-19.0 # Overture data theme theme: places diff --git a/src/config/data_variables/overture/overture_segment_europe.yaml b/src/config/data_variables/overture/overture_segment_europe.yaml index 06dc3ae..169d68d 100644 --- a/src/config/data_variables/overture/overture_segment_europe.yaml +++ b/src/config/data_variables/overture/overture_segment_europe.yaml @@ -1,6 +1,6 @@ collection: # Overture release version - version: 2024-09-18.0 + version: 2025-01-22.0 # Overture data theme theme: transportation @@ -9,19 +9,8 @@ collection: type: segment # Table in local database where data will be written - local_result_table: temporal.segment_europe + local_result_table: temporal.segments # A SQL query which returns the region's bounding geometry (omit terminating semicolon) # Leave this empty if global data is to be fetched region: "SELECT ST_Union(geom) AS geom FROM poi.geom_ref WHERE id = 'geofence_active_mobility'" - -preparation: - # Name of text file containing a list of URLs to download DEM files - dem_source_list: dem_source_list_eu.txt - - # Coefficients specifying the cost of cycling on various surfaces - cycling_surfaces: - pavingStones: '0.2' - unpaved: '0.2' - gravel: '0.3' - dirt: '0.4' diff --git a/src/config/data_variables/overture_place/overture_place_europe.yaml b/src/config/data_variables/overture_place/overture_place_europe.yaml new file mode 100644 index 0000000..f2f54b3 --- /dev/null +++ b/src/config/data_variables/overture_place/overture_place_europe.yaml @@ -0,0 +1,37 @@ +preparation: + # Table in local database where data was written during collection + local_source_table: temporal.place_europe + + # Table in local database where data will be written + local_result_table: temporal.place_europe_retail + + # A SQL query which returns the region's bounding geometry (omit terminating semicolon) + # Leave this empty if global data is to be fetched + region: "SELECT ST_Union(geom) AS geom FROM poi.geom_ref WHERE id = 'geofence_active_mobility'" + + # List of categories to produce and their associated Overture taxonomy hierarchy + # Reference: https://github.com/OvertureMaps/schema/blob/main/docs/schema/concepts/by-theme/places/overture_categories.csv + categories: + auto_parts_and_supply_store: [retail, auto_parts_and_supply_store] + beverage_store: [retail, beverage_store] + boat_parts_and_supply_store: [retail, boat_parts_and_supply_store] + carpet_store: [retail, carpet_store] + distillery: [retail, distillery] + drugstore: [retail, drugstore] + flooring_store: [retail, flooring_store] + food: [retail, food] + health_market: [retail, health_market] + hearing_aid_provider: [retail, hearing_aid_provider] + herb_and_spice_shop: [retail, herb_and_spice_shop] + honey_farm_shop: [retail, honey_farm_shop] + meat_shop: [retail, meat_shop] + olive_oil: [retail, olive_oil] + party_supply: [retail, party_supply] + pharmacy: [retail, pharmacy] + popcorn_shop: [retail, popcorn_shop] + seafood_market: [retail, seafood_market] + shopping: [retail, shopping] + tire_shop: [retail, tire_shop] + water_store: [retail, water_store] + + # public_service_and_government: [public_service_and_government] diff --git a/src/config/data_variables/overture_street_network/overture_street_network_europe.yaml b/src/config/data_variables/overture_street_network/overture_street_network_europe.yaml new file mode 100644 index 0000000..0238568 --- /dev/null +++ b/src/config/data_variables/overture_street_network/overture_street_network_europe.yaml @@ -0,0 +1,27 @@ +preparation: + # Currently, the preparation script only reads segments from temporal.segments and writes the output to basic.segment + # Currently, the preparation script only reads connectors from temporal.connectors and writes the output to basic.connector + + # A SQL query which returns the region's bounding geometry (omit terminating semicolon) + region: "SELECT ST_Union(geom) AS geom FROM poi.geom_ref WHERE id = 'geofence_active_mobility'" + + # Coefficients specifying the cost of cycling on various surfaces + cycling_surfaces: + unpaved: '0.2' + gravel: '0.3' + dirt: '0.4' + paving_stones: '0.2' + + # Coefficients specifying default speed limits for various road classes + default_speed_limits: + motorway: 80 + primary: 50 + secondary: 50 + tertiary: 50 + residential: 30 + living_street: 30 + trunk: 60 + unclassified: 50 + service: 30 + track: 30 + unknown: 30 diff --git a/src/config/overture_categories.csv b/src/config/overture_categories.csv new file mode 100644 index 0000000..c6e65fd --- /dev/null +++ b/src/config/overture_categories.csv @@ -0,0 +1,2118 @@ +Category code; Overture Taxonomy +eat_and_drink; [eat_and_drink] +restaurant; [eat_and_drink,restaurant] +afghan_restaurant; [eat_and_drink,restaurant,afghan_restaurant] +african_restaurant; [eat_and_drink,restaurant,african_restaurant] +ethiopian_restaurant; [eat_and_drink,restaurant,african_restaurant,ethiopian_restaurant] +senegalese_restaurant; [eat_and_drink,restaurant,african_restaurant,senegalese_restaurant] +south_african_restaurant; [eat_and_drink,restaurant,african_restaurant,south_african_restaurant] +moroccan_restaurant; [eat_and_drink,restaurant,african_restaurant,moroccan_restaurant] +nigerian_restaurant; [eat_and_drink,restaurant,african_restaurant,nigerian_restaurant] +american_restaurant; [eat_and_drink,restaurant,american_restaurant] +arabian_restaurant; [eat_and_drink,restaurant,arabian_restaurant] +belgian_restaurant; [eat_and_drink,restaurant,belgian_restaurant] +latin_american_restaurant; [eat_and_drink,restaurant,latin_american_restaurant] +argentine_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,argentine_restaurant] +belizean_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,belizean_restaurant] +bolivian_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,bolivian_restaurant] +brazilian_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,brazilian_restaurant] +chilean_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,chilean_restaurant] +colombian_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,colombian_restaurant] +costa_rican_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,costa_rican_restaurant] +cuban_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,cuban_restaurant] +ecuadorian_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,ecuadorian_restaurant] +guatemalan_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,guatemalan_restaurant] +honduran_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,honduran_restaurant] +mexican_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,mexican_restaurant] +nicaraguan_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,nicaraguan_restaurant] +panamanian_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,panamanian_restaurant] +paraguayan_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,paraguayan_restaurant] +peruvian_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,peruvian_restaurant] +puerto_rican_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,puerto_rican_restaurant] +salvadoran_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,salvadoran_restaurant] +texmex_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,texmex_restaurant] +uruguayan_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,uruguayan_restaurant] +venezuelan_restaurant; [eat_and_drink,restaurant,latin_american_restaurant,venezuelan_restaurant] +middle_eastern_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant] +armenian_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,armenian_restaurant] +azerbaijani_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,azerbaijani_restaurant] +egyptian_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,egyptian_restaurant] +georgian_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,georgian_restaurant] +israeli_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,israeli_restaurant] +kofta_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,kofta_restaurant] +kurdish_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,kurdish_restaurant] +lebanese_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,lebanese_restaurant] +persian_iranian_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,persian_iranian_restaurant] +syrian_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,syrian_restaurant] +turkish_restaurant; [eat_and_drink,restaurant,middle_eastern_restaurant,turkish_restaurant] +asian_restaurant; [eat_and_drink,restaurant,asian_restaurant] +asian_fusion_restaurant; [eat_and_drink,restaurant,asian_restaurant,asian_fusion_restaurant] +pan_asian_restaurant; [eat_and_drink,restaurant,asian_restaurant,pan_asian_restaurant] +burmese_restaurant; [eat_and_drink,restaurant,asian_restaurant,burmese_restaurant] +cambodian_restaurant; [eat_and_drink,restaurant,asian_restaurant,cambodian_restaurant] +chinese_restaurant; [eat_and_drink,restaurant,asian_restaurant,chinese_restaurant] +dim_sum_restaurant; [eat_and_drink,restaurant,asian_restaurant,dim_sum_restaurant] +filipino_restaurant; [eat_and_drink,restaurant,asian_restaurant,filipino_restaurant] +indo_chinese_restaurant; [eat_and_drink,restaurant,asian_restaurant,indo_chinese_restaurant] +indonesian_restaurant; [eat_and_drink,restaurant,asian_restaurant,indonesian_restaurant] +japanese_restaurant; [eat_and_drink,restaurant,asian_restaurant,japanese_restaurant] +korean_restaurant; [eat_and_drink,restaurant,asian_restaurant,korean_restaurant] +sushi_restaurant; [eat_and_drink,restaurant,asian_restaurant,sushi_restaurant] +laotian_restaurant; [eat_and_drink,restaurant,asian_restaurant,laotian_restaurant] +malaysian_restaurant; [eat_and_drink,restaurant,asian_restaurant,malaysian_restaurant] +mongolian_restaurant; [eat_and_drink,restaurant,asian_restaurant,mongolian_restaurant] +noodles_restaurant; [eat_and_drink,restaurant,asian_restaurant,noodles_restaurant] +singaporean_restaurant; [eat_and_drink,restaurant,asian_restaurant,singaporean_restaurant] +taiwanese_restaurant; [eat_and_drink,restaurant,asian_restaurant,taiwanese_restaurant] +thai_restaurant; [eat_and_drink,restaurant,asian_restaurant,thai_restaurant] +vietnamese_restaurant; [eat_and_drink,restaurant,asian_restaurant,vietnamese_restaurant] +hong_kong_style_cafe; [eat_and_drink,restaurant,asian_restaurant,hong_kong_style_café] +australian_restaurant; [eat_and_drink,restaurant,australian_restaurant] +austrian_restaurant; [eat_and_drink,restaurant,austrian_restaurant] +bangladeshi_restaurant; [eat_and_drink,restaurant,bangladeshi_restaurant] +indian_restaurant; [eat_and_drink,restaurant,indian_restaurant] +basque_restaurant; [eat_and_drink,restaurant,basque_restaurant] +british_restaurant; [eat_and_drink,restaurant,british_restaurant] +eastern_european_restaurant; [eat_and_drink,restaurant,eastern_european_restaurant] +belarusian_restaurant; [eat_and_drink,restaurant,eastern_european_restaurant,belarusian_restaurant] +bulgarian_restaurant; [eat_and_drink,restaurant,eastern_european_restaurant,bulgarian_restaurant] +romanian_restaurant; [eat_and_drink,restaurant,eastern_european_restaurant,romanian_restaurant] +tatar_restaurant; [eat_and_drink,restaurant,eastern_european_restaurant,tatar_restaurant] +ukrainian_restaurant; [eat_and_drink,restaurant,eastern_european_restaurant,ukrainian_restaurant] +french_restaurant; [eat_and_drink,restaurant,french_restaurant] +cajun_creole_restaurant; [eat_and_drink,restaurant,cajun_and_creole_restaurant] +canadian_restaurant; [eat_and_drink,restaurant,canadian_restaurant] +caribbean_restaurant; [eat_and_drink,restaurant,caribbean_restaurant] +dominican_restaurant; [eat_and_drink,restaurant,caribbean_restaurant,dominican_restaurant] +haitian_restaurant; [eat_and_drink,restaurant,caribbean_restaurant,haitian_restaurant] +jamaican_restaurant; [eat_and_drink,restaurant,caribbean_restaurant,jamaican_restaurant] +trinidadian_restaurant; [eat_and_drink,restaurant,caribbean_restaurant,trinidadian_restaurant] +german_restaurant; [eat_and_drink,restaurant,german_restaurant] +catalan_restaurant; [eat_and_drink,restaurant,catalan_restaurant] +italian_restaurant; [eat_and_drink,restaurant,italian_restaurant] +czech_restaurant; [eat_and_drink,restaurant,czech_restaurant] +mediterranean_restaurant; [eat_and_drink,restaurant,mediterranean_restaurant] +greek_restaurant; [eat_and_drink,restaurant,mediterranean_restaurant,greek_restaurant] +guamanian_restaurant; [eat_and_drink,restaurant,guamanian_restaurant] +hawaiian_restaurant; [eat_and_drink,restaurant,hawaiian_restaurant] +himalayan_nepalese_restaurant; [eat_and_drink,restaurant,himalayan_nepalese_restaurant] +hungarian_restaurant; [eat_and_drink,restaurant,hungarian_restaurant] +iberian_restaurant; [eat_and_drink,restaurant,iberian_restaurant] +irish_restaurant; [eat_and_drink,restaurant,irish_restaurant] +jewish_restaurant; [eat_and_drink,restaurant,jewish_restaurant] +international_restaurant; [eat_and_drink,restaurant,international_restaurant] +european_restaurant; [eat_and_drink,restaurant,european_restaurant] +oriental_restaurant; [eat_and_drink,restaurant,oriental_restaurant] +pakistani_restaurant; [eat_and_drink,restaurant,pakistani_restaurant] +polish_restaurant; [eat_and_drink,restaurant,polish_restaurant] +polynesian_restaurant; [eat_and_drink,restaurant,polynesian_restaurant] +portuguese_restaurant; [eat_and_drink,restaurant,portuguese_restaurant] +russian_restaurant; [eat_and_drink,restaurant,russian_restaurant] +scandinavian_restaurant; [eat_and_drink,restaurant,scandinavian_restaurant] +danish_restaurant; [eat_and_drink,restaurant,scandinavian_restaurant,danish_restaurant] +norwegian_restaurant; [eat_and_drink,restaurant,scandinavian_restaurant,norwegian_restaurant] +scottish_restaurant; [eat_and_drink,restaurant,scottish_restaurant] +seafood_restaurant; [eat_and_drink,restaurant,seafood_restaurant] +serbo_croation_restaurant; [eat_and_drink,restaurant,serbo_croatian_restaurant] +slovakian_restaurant; [eat_and_drink,restaurant,slovakian_restaurant] +southern_restaurant; [eat_and_drink,restaurant,southern_restaurant] +spanish_restaurant; [eat_and_drink,restaurant,spanish_restaurant] +sri_lankan_restaurant; [eat_and_drink,restaurant,sri_lankan_restaurant] +swiss_restaurant; [eat_and_drink,restaurant,swiss_restaurant] +uzbek_restaurant; [eat_and_drink,restaurant,uzbek_restaurant] +molecular_gastronomy_restaurant; [eat_and_drink,restaurant,molecular_gastronomy_restaurant] +haute_cuisine_restaurant; [eat_and_drink,restaurant,haute_cuisine_restaurant] +salad_bar; [eat_and_drink,restaurant,salad_bar] +brasserie; [eat_and_drink,restaurant,brasserie] +buffet_restaurant; [eat_and_drink,restaurant,buffet_restaurant] +barbecue_restaurant; [eat_and_drink,restaurant,barbecue_restaurant] +burger_restaurant; [eat_and_drink,restaurant,burger_restaurant] +canteen; [eat_and_drink,restaurant,canteen] +cafeteria; [eat_and_drink,restaurant,cafeteria] +cheesesteak_restaurant; [eat_and_drink,restaurant,cheesesteak_restaurant] +chicken_restaurant; [eat_and_drink,restaurant,chicken_restaurant] +chicken_wings_restaurant; [eat_and_drink,restaurant,chicken_wings_restaurant] +dog_meat_restaurant; [eat_and_drink,restaurant,dog_meat_restaurant] +dumpling_restaurant; [eat_and_drink,restaurant,dumpling_restaurant] +fast_food_restaurant; [eat_and_drink,restaurant,fast_food_restaurant] +fishchbroetchen_restaurant; [eat_and_drink,restaurant,fischbroetchen_restaurant] +acai_bowls; [eat_and_drink,restaurant,acai_bowls] +fish_restaurant; [eat_and_drink,restaurant,fish_restaurant] +fish_and_chips_restaurant; [eat_and_drink,restaurant,fish_and_chips_restaurant] +fondue_restaurant; [eat_and_drink,restaurant,fondue_restaurant] +gluten_free_restaurant; [eat_and_drink,restaurant,gluten_free_restaurant] +baozi_restaurant; [eat_and_drink,restaurant,baozi_restaurant] +halal_restaurant; [eat_and_drink,restaurant,halal_restaurant] +bistro; [eat_and_drink,restaurant,bistro] +health_food_restaurant; [eat_and_drink,restaurant,health_food_restaurant] +kosher_restaurant; [eat_and_drink,restaurant,kosher_restaurant] +comfort_food_restaurant; [eat_and_drink,restaurant,comfort_food_restaurant] +meat_restaurant; [eat_and_drink,restaurant,meat_restaurant] +curry_sausage_restaurant; [eat_and_drink,restaurant,curry_sausage_restaurant] +diner; [eat_and_drink,restaurant,diner] +diy_foods_restaurant; [eat_and_drink,restaurant,diy_foods_restaurant] +doner_kebab; [eat_and_drink,restaurant,doner_kebab] +empanadas; [eat_and_drink,restaurant,empanadas] +nasi_restaurant; [eat_and_drink,restaurant,nasi_restaurant] +flatbread_restaurant; [eat_and_drink,restaurant,flatbread_restaurant] +food_court; [eat_and_drink,restaurant,food_court] +gastropub; [eat_and_drink,restaurant,gastropub] +pizza_restaurant; [eat_and_drink,restaurant,pizza_restaurant] +pop_up_restaurant; [eat_and_drink,restaurant,pop_up_restaurant] +poutinerie_restaurant; [eat_and_drink,restaurant,poutinerie_restaurant] +vegan_restaurant; [eat_and_drink,restaurant,vegan_restaurant] +vegetarian_restaurant; [eat_and_drink,restaurant,vegetarian_restaurant] +wok_restaurant; [eat_and_drink,restaurant,wok_restaurant] +wrap_restaurant; [eat_and_drink,restaurant,wrap_restaurant] +piadina_restaurant; [eat_and_drink,restaurant,piadina_restaurant] +poke; [eat_and_drink,restaurant,poke_restaurant] +pigs_trotters_restaurant; [eat_and_drink,restaurant,pigs_trotters_restaurant] +potato_restaurant; [eat_and_drink,restaurant,potato_restaurant] +rotisserie_chicken_restaurant; [eat_and_drink,restaurant,rotisserie_chicken_restaurant] +schnitzel_restaurant; [eat_and_drink,restaurant,schnitzel_restaurant] +soul_food; [eat_and_drink,restaurant,soul_food] +steakhouse; [eat_and_drink,restaurant,steakhouse] +supper_club; [eat_and_drink,restaurant,supper_club] +tapas_bar; [eat_and_drink,restaurant,tapas_bar] +venison_restaurant; [eat_and_drink,restaurant,venison_restaurant] +wild_game_meats_restaurant; [eat_and_drink,restaurant,wild_game_meats_restaurant] +falafel_restaurant; [eat_and_drink,restaurant,falafel_restaurant] +taco_restaurant; [eat_and_drink,restaurant,taco_restaurant] +hot_dog_restaurant; [eat_and_drink,restaurant,hot_dog_restaurant] +live_and_raw_food_restaurant; [eat_and_drink,restaurant,live_and_raw_food_restaurant] +soup_restaurant; [eat_and_drink,restaurant,soup_restaurant] +theme_restaurant; [eat_and_drink,restaurant,theme_restaurant] +bar_and_grill_restaurant; [eat_and_drink,restaurant,bar_and_grill_restaurant] +meatball_restaurant; [eat_and_drink,restaurant,meatball_restaurant] +waffle_restaurant; [eat_and_drink,restaurant,waffle_restaurant] +breakfast_and_brunch_restaurant; [eat_and_drink,restaurant,breakfast_and_brunch_restaurant] +pancake_house; [eat_and_drink,restaurant,breakfast_and_brunch_restaurant,pancake_house] +bagel_restaurant; [eat_and_drink,restaurant,breakfast_and_brunch_restaurant,bagel_restaurant] +baguettes; [eat_and_drink,restaurant,breakfast_and_brunch_restaurant,baguettes] +bar; [eat_and_drink,bar] +airport_lounge; [eat_and_drink,bar,airport_lounge] +beach_bar; [eat_and_drink,bar,beach_bar] +beer_bar; [eat_and_drink,bar,beer_bar] +beer_garden; [eat_and_drink,bar,beer_garden] +brewery; [eat_and_drink,bar,brewery] +bubble_tea; [eat_and_drink,bar,bubble_tea] +champagne_bar; [eat_and_drink,bar,champagne_bar] +cidery; [eat_and_drink,bar,cidery] +cigar_bar; [eat_and_drink,bar,cigar_bar] +cocktail_bar; [eat_and_drink,bar,cocktail_bar] +dive_bar; [eat_and_drink,bar,dive_bar] +drive_thru_bar; [eat_and_drink,bar,drive_thru_bar] +gay_bar; [eat_and_drink,bar,gay_bar] +hookah_bar; [eat_and_drink,bar,hookah_bar] +hotel_bar; [eat_and_drink,bar,hotel_bar] +irish_pub; [eat_and_drink,bar,irish_pub] +kombucha; [eat_and_drink,bar,kombucha] +lounge; [eat_and_drink,bar,lounge] +milk_bar; [eat_and_drink,bar,milk_bar] +milkshake_bar; [eat_and_drink,bar,milkshake_bar] +pub; [eat_and_drink,bar,pub] +sake_bar; [eat_and_drink,bar,sake_bar] +speakeasy; [eat_and_drink,bar,speakeasy] +sports_bar; [eat_and_drink,bar,sports_bar] +sugar_shack; [eat_and_drink,bar,sugar_shack] +tabac; [eat_and_drink,bar,tabac] +tiki_bar; [eat_and_drink,bar,tiki_bar] +vermouth_bar; [eat_and_drink,bar,vermouth_bar] +whiskey_bar; [eat_and_drink,bar,whiskey_bar] +wine_bar; [eat_and_drink,bar,wine_bar] +smoothie_juice_bar; [eat_and_drink,bar,smoothie_juice_bar] +piano_bar; [eat_and_drink,bar,piano_bar] +cafe; [eat_and_drink,cafe] +coffee_roastery; [eat_and_drink,cafe,coffee_roastery] +tea_room; [eat_and_drink,cafe,tea_room] +coffee_shop; [eat_and_drink,cafe,coffee_shop] +accommodation; [accommodation] +bed_and_breakfast; [accommodation,bed_and_breakfast] +cabin; [accommodation,cabin] +campground; [accommodation,campground] +cottage; [accommodation,cottage] +guest_house; [accommodation,guest_house] +health_retreats; [accommodation,health_retreats] +holiday_rental_home; [accommodation,holiday_rental_home] +hostel; [accommodation,hostel] +hotel; [accommodation,hotel] +inn; [accommodation,inn] +lodge; [accommodation,lodge] +motel; [accommodation,motel] +mountain_huts; [accommodation,mountain_huts] +resort; [accommodation,resort] +beach_resort; [accommodation,resort,beach_resort] +rv_park; [accommodation,rv_park] +service_apartments; [accommodation,service_apartments] +automotive; [automotive] +automotive_dealer; [automotive,automotive_dealer] +car_buyer; [automotive,car_buyer] +car_dealer; [automotive,automotive_dealer,car_dealer] +commercial_vehicle_dealer; [automotive,automotive_dealer,commercial_vehicle_dealer] +golf_cart_dealer; [automotive,automotive_dealer,golf_cart_dealer] +motorcycle_dealer; [automotive,automotive_dealer,motorcycle_dealer] +motorsport_vehicle_dealer; [automotive,automotive_dealer,motorsport_vehicle_dealer] +recreational_vehicle_dealer; [automotive,automotive_dealer,recreational_vehicle_dealer] +scooter_dealers; [automotive,automotive_dealer,scooter_dealers] +trailer_dealer; [automotive,automotive_dealer,trailer_dealer] +truck_dealer; [automotive,automotive_dealer,truck_dealer] +used_car_dealer; [automotive,automotive_dealer,used_car_dealer] +automotive_services_and_repair; [automotive,automotive_services_and_repair] +auto_body_shop; [automotive,automotive_services_and_repair,auto_body_shop] +auto_customization; [automotive,automotive_services_and_repair,auto_customization] +auto_detailing; [automotive,automotive_services_and_repair,auto_detailing] +auto_electrical_repair; [automotive,automotive_services_and_repair,auto_electrical_repair] +auto_glass_service; [automotive,automotive_services_and_repair,auto_glass_service] +car_window_tinting; [automotive,automotive_services_and_repair,auto_glass_service,car_window_tinting] +auto_restoration_services; [automotive,automotive_services_and_repair,auto_restoration_services] +auto_security; [automotive,automotive_services_and_repair,auto_security] +automobile_registration_service; [automotive,automotive_services_and_repair,automobile_registration_service] +automotive_consultant; [automotive,automotive_services_and_repair,automotive_consultant] +automotive_storage_facility; [automotive,automotive_services_and_repair,automotive_storage_facility] +brake_service_and_repair; [automotive,automotive_services_and_repair,brake_service_and_repair] +car_inspector; [automotive,automotive_services_and_repair,car_inspection] +car_stereo_installation; [automotive,automotive_services_and_repair,car_stereo_installation] +car_wash; [automotive,automotive_services_and_repair,car_wash] +diy_auto_shop; [automotive,automotive_services_and_repair,diy_auto_shop] +emissions_inspection; [automotive,automotive_services_and_repair,emissions_inspection] +engine_repair_service; [automotive,automotive_services_and_repair,engine_repair_service] +exhaust_and_muffler_repair; [automotive,automotive_services_and_repair,exhaust_and_muffler_repair] +hybrid_car_repair; [automotive,automotive_services_and_repair,hybrid_car_repair] +motorcycle_repair; [automotive,automotive_services_and_repair,motorcycle_repair] +motorsport_vehicle_repair; [automotive,motorsport_vehicle_repair] +oil_change_station; [automotive,automotive_services_and_repair,oil_change_station] +recreation_vehicle_repair; [automotive,automotive_services_and_repair,recreation_vehicle_repair] +roadside_assistance; [automotive,automotive_services_and_repair,roadside_assistance] +emergency_roadside_service; [automotive,automotive_services_and_repair,roadside_assistance,emergency_roadside_service] +mobile_dent_repair; [automotive,automotive_services_and_repair,roadside_assistance,mobile_dent_repair] +tire_dealer_and_repair; [automotive,automotive_services_and_repair,tire_dealer_and_repair] +transmission_repair; [automotive,automotive_services_and_repair,transmission_repair] +towing_service; [automotive,automotive_services_and_repair,towing_service] +trailer_repair; [automotive,automotive_services_and_repair,trailer_repair] +truck_repair; [automotive,automotive_services_and_repair,truck_repair] +auto_upholstery; [automotive,automotive_services_and_repair,auto_upholstery] +vehicle_wrap; [automotive,automotive_services_and_repair,vehicle_wrap] +vehicle_shipping; [automotive,automotive_services_and_repair,vehicle_shipping] +wheel_and_rim_repair; [automotive,automotive_services_and_repair,wheel_and_rim_repair] +automotive_wheel_polishing_service; [automotive,automotive_services_and_repair,automotive_wheel_polishing_service] +windshield_installation_and_repair; [automotive,automotive_services_and_repair,windshield_installation_and_repair] +automotive_parts_and_accessories; [automotive,automotive_parts_and_accessories] +recreational_vehicle_parts_and_accessories; [automotive,automotive_parts_and_accessories,recreational_vehicle_parts_and_accessories] +car_stereo_store; [automotive,automotive_parts_and_accessories,car_stereo_store] +motorcycle_gear; [automotive,automotive_parts_and_accessories,motorcycle_gear] +motorsports_store; [automotive,automotive_parts_and_accessories,motorsports_store] +interlock_system; [automotive,automotive_parts_and_accessories,interlock_system] +aircraft_dealer; [automotive,aircraft_dealer] +aircraft_repair; [automotive,aircraft_services_and_repair] +aircraft_parts_and_supplies; [automotive,aircraft_parts_and_supplies] +avionics_shop; [automotive,aircraft_parts_and_supplies,avionics_shop] +boat_dealer; [automotive,boat_dealer] +boat_service_and_repair; [automotive,boat_service_and_repair] +boat_parts_and_accessories; [automotive,boat_parts_and_accessories] +gas_station; [automotive,gas_station] +truck_gas_station; [automotive,gas_station,truck_gas_station] +fuel_dock; [automotive,gas_station,fuel_dock] +ev_charging_station; [automotive,electric_vehicle_charging_station] +truck_stop; [automotive,truck_stop] +automobile_leasing; [automotive,automobile_leasing] +automotive_repair; [automotive,automotive_repair] +auto_company; [automotive,auto_company] +motorcycle_manufacturer; [automotive,motorcycle_manufacturer] +arts_and_entertainment; [arts_and_entertainment] +adult_entertainment; [arts_and_entertainment,adult_entertainment] +erotic_massage; [arts_and_entertainment,adult_entertainment,erotic_massage] +strip_club; [arts_and_entertainment,adult_entertainment,strip_club] +striptease_dancer; [arts_and_entertainment,adult_entertainment,striptease_dancer] +arcade; [arts_and_entertainment,arcade] +auditorium; [arts_and_entertainment,auditorium] +bar_crawl; [arts_and_entertainment,bar_crawl] +betting_center; [arts_and_entertainment,betting_center] +bingo_hall; [arts_and_entertainment,bingo_hall] +bookmakers; [arts_and_entertainment,bookmakers] +cabaret; [arts_and_entertainment,cabaret] +carousel; [arts_and_entertainment,carousel] +casino; [arts_and_entertainment,casino] +chamber_of_handicraft; [arts_and_entertainment,chamber_of_handicraft] +choir; [arts_and_entertainment,choir] +circus; [arts_and_entertainment,circus] +club_crawl; [arts_and_entertainment,club_crawl] +comedy_club; [arts_and_entertainment,comedy_club] +topic_concert_venue; [arts_and_entertainment,topic_concert_venue] +country_club; [arts_and_entertainment,country_club] +country_dance_hall; [arts_and_entertainment,country_dance_hall] +dance_club; [arts_and_entertainment,dance_club] +dinner_theater; [arts_and_entertainment,dinner_theater] +eatertainment; [arts_and_entertainment,eatertainment] +escape_rooms; [arts_and_entertainment,escape_rooms] +exhibition_and_trade_center; [arts_and_entertainment,exhibition_and_trade_center] +glass_blowing; [arts_and_entertainment,glass_blowing] +indoor_playcenter; [arts_and_entertainment,indoor_playcenter] +internet_cafe; [arts_and_entertainment,internet_cafe] +jazz_and_blues; [arts_and_entertainment,jazz_and_blues] +karaoke; [arts_and_entertainment,karaoke] +laser_tag; [arts_and_entertainment,laser_tag] +makerspace; [arts_and_entertainment,makerspace] +marching_band; [arts_and_entertainment,marching_band] +music_venue; [arts_and_entertainment,music_venue] +musical_band_orchestras_and_symphonies; [arts_and_entertainment,musical_band_orchestras_and_symphonies] +opera_and_ballet; [arts_and_entertainment,opera_and_ballet] +paint_and_sip; [arts_and_entertainment,paint_and_sip] +paintball; [arts_and_entertainment,paintball] +performing_arts; [arts_and_entertainment,performing_arts] +planetarium; [arts_and_entertainment,planetarium] +rodeo; [arts_and_entertainment,rodeo] +salsa_club; [arts_and_entertainment,salsa_club] +wildlife_sanctuary; [arts_and_entertainment,wildlife_sanctuary] +stadium_arena; [arts_and_entertainment,stadium_arena] +baseball_stadium; [arts_and_entertainment,stadium_arena,baseball_stadium] +basketball_stadium; [arts_and_entertainment,stadium_arena,basketball_stadium] +cricket_ground; [arts_and_entertainment,stadium_arena,cricket_ground] +football_stadium; [arts_and_entertainment,stadium_arena,football_stadium] +hockey_arena; [arts_and_entertainment,stadium_arena,hockey_arena] +rugby_stadium; [arts_and_entertainment,stadium_arena,rugby_stadium] +soccer_stadium; [arts_and_entertainment,stadium_arena,soccer_stadium] +tennis_stadium; [arts_and_entertainment,stadium_arena,tennis_stadium] +track_stadium; [arts_and_entertainment,stadium_arena,track_stadium] +studio_taping; [arts_and_entertainment,studio_taping] +theaters_and_performance_venues; [arts_and_entertainment,theaters_and_performance_venues] +theatre; [arts_and_entertainment,theaters_and_performance_venues,theatre] +ticket_sales; [arts_and_entertainment,ticket_sales] +virtual_reality_center; [arts_and_entertainment,virtual_reality_center] +water_park; [arts_and_entertainment,water_park] +cinema; [arts_and_entertainment,cinema] +drive_in_theater; [arts_and_entertainment,cinema,drive_in_theater] +outdoor_movies; [arts_and_entertainment,cinema,outdoor_movies] +farm; [arts_and_entertainment,farm] +attraction_farm; [arts_and_entertainment,farm,attraction_farm] +orchard; [arts_and_entertainment,farm,orchard] +pick_your_own_farm; [arts_and_entertainment,farm,pick_your_own_farm] +poultry_farm; [arts_and_entertainment,farm,poultry_farm] +ranch; [arts_and_entertainment,farm,ranch] +festival; [arts_and_entertainment,festival] +fair; [arts_and_entertainment,festival,fair] +film_festivals_and_organizations; [arts_and_entertainment,festival,film_festivals_and_organizations] +general_festivals; [arts_and_entertainment,festival,general_festivals] +holiday_market; [arts_and_entertainment,festival,holiday_market] +music_festivals_and_organizations; [arts_and_entertainment,festival,music_festivals_and_organizations] +trade_fair; [arts_and_entertainment,festival,trade_fair] +social_club; [arts_and_entertainment,social_club] +fraternal_organization; [arts_and_entertainment,social_club,fraternal_organization] +veterans_organization; [arts_and_entertainment,social_club,veterans_organization] +supernatural_reading; [arts_and_entertainment,supernatural_reading] +astrologer; [arts_and_entertainment,supernatural_reading,astrologer] +mystic; [arts_and_entertainment,supernatural_reading,mystic] +psychic; [arts_and_entertainment,supernatural_reading,psychic] +psychic_medium; [arts_and_entertainment,supernatural_reading,psychic_medium] +attractions_and_activities; [attractions_and_activities] +amusement_park; [attractions_and_activities,amusement_park] +aquarium; [attractions_and_activities,aquarium] +architecture; [attractions_and_activities,architecture] +art_gallery; [attractions_and_activities,art_gallery] +atv_rentals_and_tours; [attractions_and_activities,atv_rentals_and_tours] +axe_throwing; [attractions_and_activities,axe_throwing] +backpacking_area; [attractions_and_activities,backpacking_area] +beach; [attractions_and_activities,beach] +beach_combing_area; [attractions_and_activities,beach_combing_area] +boat_rental_and_training; [attractions_and_activities,boat_rental_and_training] +boating_places; [attractions_and_activities,boating_places] +bobsledding_field; [attractions_and_activities,bobsledding_field] +botanical_garden; [attractions_and_activities,botanical_garden] +bungee_jumping_center; [attractions_and_activities,bungee_jumping_center] +canyon; [attractions_and_activities,canyon] +castle; [attractions_and_activities,castle] +cave; [attractions_and_activities,cave] +challenge_courses_center; [attractions_and_activities,challenge_courses_center] +cliff_jumping_center; [attractions_and_activities,cliff_jumping_center] +climbing_service; [attractions_and_activities,climbing_service] +crater; [attractions_and_activities,crater] +cultural_center; [attractions_and_activities,cultural_center] +fishing_charter; [attractions_and_activities,fishing_charter] +flyboarding_rental; [attractions_and_activities,flyboarding_rental] +fort; [attractions_and_activities,fort] +fountain; [attractions_and_activities,fountain] +go_kart_track; [attractions_and_activities,go_kart_track] +haunted_house; [attractions_and_activities,haunted_house] +high_gliding_center; [attractions_and_activities,high_gliding_center] +horseback_riding_service; [attractions_and_activities,horseback_riding_service] +hot_air_balloons_tour; [attractions_and_activities,hot_air_balloons_tour] +hot_springs; [attractions_and_activities,hot_springs] +jet_skis_rental; [attractions_and_activities,jet_skis_rental] +kiteboarding_instruction; [attractions_and_activities,kiteboarding_instruction] +lake; [attractions_and_activities,lake] +landmark_and_historical_building; [attractions_and_activities,landmark_and_historical_building] +lighthouse; [attractions_and_activities,lighthouse] +lookout; [attractions_and_activities,lookout] +marina; [attractions_and_activities,marina] +monument; [attractions_and_activities,monument] +mountain_bike_parks; [attractions_and_activities,mountain_bike_parks] +observatory; [attractions_and_activities,observatory] +paddleboard_rental; [attractions_and_activities,paddleboard_rental] +palace; [attractions_and_activities,palace] +parasailing_ride_service; [attractions_and_activities,parasailing_ride_service] +plaza; [attractions_and_activities,plaza] +rafting_kayaking_area; [attractions_and_activities,rafting_kayaking_area] +rock_climbing_spot; [attractions_and_activities,rock_climbing_spot] +ruin; [attractions_and_activities,ruin] +sailing_area; [attractions_and_activities,sailing_area] +sand_dune; [attractions_and_activities,sand_dune] +scavenger_hunts_provider; [attractions_and_activities,scavenger_hunts_provider] +sculpture_statue; [attractions_and_activities,sculpture_statue] +ski_area; [attractions_and_activities,ski_area] +skyline; [attractions_and_activities,skyline] +sledding_rental; [attractions_and_activities,sledding_rental] +snorkeling_equipment_rental; [attractions_and_activities,snorkeling_equipment_rental] +snorkeling; [attractions_and_activities,snorkeling] +snowboarding_center; [attractions_and_activities,snowboarding_center] +stargazing_area; [attractions_and_activities,stargazing_area] +street_art; [attractions_and_activities,street_art] +trail; [attractions_and_activities,trail] +hiking_trail; [attractions_and_activities,trail,hiking_trail] +mountain_bike_trails; [attractions_and_activities,trail,mountain_biking_trails] +waterfall; [attractions_and_activities,waterfall] +surfing; [attractions_and_activities,surfing] +surfboard_rental; [attractions_and_activities,surfing,surfboard_rental] +windsurfing_center; [attractions_and_activities,surfing,windsurfing_center] +ziplining_center; [attractions_and_activities,ziplining_center] +zoo; [attractions_and_activities,zoo] +petting_zoo; [attractions_and_activities,zoo,petting_zoo] +museum; [attractions_and_activities,museum] +art_museum; [attractions_and_activities,museum,art_museum] +asian_art_museum; [attractions_and_activities,museum,art_museum,asian_art_museum] +cartooning_museum; [attractions_and_activities,museum,art_museum,cartooning_museum] +contemporary_art_museum; [attractions_and_activities,museum,art_museum,contemporary_art_museum] +children's_museum; [attractions_and_activities,museum,children's_museum] +costume_museum; [attractions_and_activities,museum,art_museum,costume_museum] +decorative_arts_museum; [attractions_and_activities,museum,art_museum,decorative_arts_museum] +design_museum; [attractions_and_activities,museum,art_museum,design_museum] +modern_art_museum; [attractions_and_activities,museum,art_museum,modern_art_museum] +photography_museum; [attractions_and_activities,museum,art_museum,photography_museum] +textile_museum; [attractions_and_activities,museum,art_museum,textile_museum] +history_museum; [attractions_and_activities,museum,history_museum] +civilization_museum; [attractions_and_activities,museum,history_museum,civilization_museum] +community_museum; [attractions_and_activities,museum,history_museum,community_museum] +military_museum; [attractions_and_activities,museum,military_museum] +national_museum; [attractions_and_activities,museum,national_museum] +science_museum; [attractions_and_activities,museum,science_museum] +computer_museum; [attractions_and_activities,museum,science_museum,computer_museum] +state_museum; [attractions_and_activities,museum,state_museum] +aviation_museum; [attractions_and_activities,museum,aviation_museum] +sports_museum; [attractions_and_activities,museum,sports_museum] +park; [attractions_and_activities,park] +dog_park; [attractions_and_activities,park,dog_park] +memorial_park; [attractions_and_activities,park,memorial_park] +national_park; [attractions_and_activities,park,national_park] +state_park; [attractions_and_activities,park,state_park] +active_life; [active_life] +sports_and_recreation_venue; [active_life,sports_and_recreation_venue] +adventure_sports_center; [active_life,sports_and_recreation_venue,adventure_sports_center] +american_football_field; [active_life,sports_and_recreation_venue,american_football_field] +airsoft_fields; [active_life,sports_and_recreation_venue,airsoft_fields] +archery_range; [active_life,sports_and_recreation_venue,archery_range] +atv_recreation_park; [active_life,sports_and_recreation_venue,atv_recreation_park] +badminton_court; [active_life,sports_and_recreation_venue,badminton_court] +baseball_field; [active_life,sports_and_recreation_venue,baseball_field] +basketball_court; [active_life,sports_and_recreation_venue,basketball_court] +batting_cage; [active_life,sports_and_recreation_venue,batting_cage] +beach_volleyball_court; [active_life,sports_and_recreation_venue,beach_volleyball_court] +bicycle_path; [active_life,sports_and_recreation_venue,bicycle_path] +bocce_ball_court; [active_life,sports_and_recreation_venue,bocce_ball_court] +bowling_alley; [active_life,sports_and_recreation_venue,bowling_alley] +bubble_soccer_field; [active_life,sports_and_recreation_venue,bubble_soccer_field] +disc_golf_course; [active_life,sports_and_recreation_venue,disc_golf_course] +flyboarding_center; [active_life,sports_and_recreation_venue,flyboarding_center] +futsal_field; [active_life,sports_and_recreation_venue,futsal_field] +golf_course; [active_life,sports_and_recreation_venue,golf_course] +driving_range; [active_life,sports_and_recreation_venue,golf_course,driving_range] +gym; [active_life,sports_and_recreation_venue,gym] +gymnastics_center; [active_life,sports_and_recreation_venue,gymnastics_center] +handball_court; [active_life,sports_and_recreation_venue,handball_court] +hockey_field; [active_life,sports_and_recreation_venue,hockey_field] +horse_riding; [active_life,sports_and_recreation_venue,horse_riding] +equestrian_facility; [active_life,sports_and_recreation_venue,horse_riding,equestrian_facility] +horse_racing_track; [active_life,sports_and_recreation_venue,horse_riding,horse_racing_track] +skate_park; [active_life,sports_and_recreation_venue,skate_park] +skating_rink; [active_life,sports_and_recreation_venue,skating_rink] +ice_skating_rink; [active_life,sports_and_recreation_venue,skating_rink,ice_skating_rink] +roller_skating_rink; [active_life,sports_and_recreation_venue,skating_rink,roller_skating_rink] +hang_gliding_center; [active_life,sports_and_recreation_venue,hang_gliding_center] +kiteboarding; [active_life,sports_and_recreation_venue,kiteboarding] +miniature_golf_course; [active_life,sports_and_recreation_venue,miniature_golf_course] +paddleboarding_center; [active_life,sports_and_recreation_venue,paddleboarding_center] +playground; [active_life,sports_and_recreation_venue,playground] +pool_billiards; [active_life,sports_and_recreation_venue,pool_billiards] +pool_hall; [active_life,sports_and_recreation_venue,pool_billiards,pool_hall] +racquetball_court; [active_life,sports_and_recreation_venue,racquetball_court] +rock_climbing_gym; [active_life,sports_and_recreation_venue,rock_climbing_gym] +rugby_pitch; [active_life,sports_and_recreation_venue,rugby_pitch] +shooting_range; [active_life,sports_and_recreation_venue,shooting_range] +sky_diving; [active_life,sports_and_recreation_venue,sky_diving] +soccer_field; [active_life,sports_and_recreation_venue,soccer_field] +squash_court; [active_life,sports_and_recreation_venue,squash_court] +swimming_pool; [active_life,sports_and_recreation_venue,swimming_pool] +tennis_court; [active_life,sports_and_recreation_venue,tennis_court] +trampoline_park; [active_life,sports_and_recreation_venue,trampoline_park] +tubing_provider; [active_life,sports_and_recreation_venue,tubing_provider] +volleyball_court; [active_life,sports_and_recreation_venue,volleyball_court] +wildlife_hunting_range; [active_life,sports_and_recreation_venue,wildlife_hunting_range] +zorbing_center; [active_life,sports_and_recreation_venue,zorbing_center] +diving_center; [active_life,sports_and_recreation_venue,diving_center] +free_diving_center; [active_life,sports_and_recreation_venue,diving_center,free_diving_center] +scuba_diving_center; [active_life,sports_and_recreation_venue,diving_center,scuba_diving_center] +race_track; [active_life,sports_and_recreation_venue,race_track] +sports_and_fitness_instruction; [active_life,sports_and_fitness_instruction] +climbing_class; [active_life,sports_and_fitness_instruction,climbing_class] +cycling_classes; [active_life,sports_and_fitness_instruction,cycling_classes] +golf_instructor; [active_life,sports_and_fitness_instruction,golf_instructor] +paddleboarding_lessons; [active_life,sports_and_fitness_instruction,paddleboarding_lessons] +racing_experience; [active_life,sports_and_fitness_instruction,racing_experience] +rock_climbing_instructor; [active_life,sports_and_fitness_instruction,rock_climbing_instructor] +free_diving_instruction; [active_life,sports_and_fitness_instruction,diving_instruction,free_diving_instruction] +scuba_diving_instruction; [active_life,sports_and_fitness_instruction,diving_instruction,scuba_diving_instruction] +aerial_fitness_center; [active_life,sports_and_fitness_instruction,aerial_fitness_center] +barre_classes; [active_life,sports_and_fitness_instruction,barre_classes] +boot_camp; [active_life,sports_and_fitness_instruction,boot_camp] +boxing_class; [active_life,sports_and_fitness_instruction,boxing_class] +boxing_club; [active_life,sports_and_fitness_instruction,boxing_club] +boxing_gym; [active_life,sports_and_fitness_instruction,boxing_gym] +cardio_classes; [active_life,sports_and_fitness_instruction,cardio_classes] +dance_school; [active_life,sports_and_fitness_instruction,dance_school] +ems_training; [active_life,sports_and_fitness_instruction,ems_training] +fitness_trainer; [active_life,sports_and_fitness_instruction,fitness_trainer] +health_consultant; [active_life,sports_and_fitness_instruction,health_consultant] +meditation_center; [active_life,sports_and_fitness_instruction,meditation_center] +pilates_studio; [active_life,sports_and_fitness_instruction,pilates_studio] +qi_gong_studio; [active_life,sports_and_fitness_instruction,qi_gong_studio] +self_defense_classes; [active_life,sports_and_fitness_instruction,self_defense_classes] +ski_and_snowboard_school; [active_life,sports_and_fitness_instruction,ski_and_snowboard_school] +surfing_school; [active_life,sports_and_fitness_instruction,surfing_school] +swimming_instructor; [active_life,sports_and_fitness_instruction,swimming_instructor] +tai_chi_studio; [active_life,sports_and_fitness_instruction,tai_chi_studio] +yoga_instructor; [active_life,sports_and_fitness_instruction,yoga_instructor] +yoga_studio; [active_life,sports_and_fitness_instruction,yoga_studio] +sports_club_and_league; [active_life,sports_club_and_league] +amateur_sports_league; [active_life,sports_club_and_league,amateur_sports_league] +amateur_sports_team; [active_life,sports_club_and_league,amateur_sports_team] +beach_volleyball_club; [active_life,sports_club_and_league,beach_volleyball_club] +esports_league; [active_life,sports_club_and_league,esports_league] +esports_team; [active_life,sports_club_and_league,esports_team] +fencing_club; [active_life,sports_club_and_league,fencing_club] +fishing_club; [active_life,sports_club_and_league,fishing_club] +football_club; [active_life,sports_club_and_league,football_club] +go_kart_club; [active_life,sports_club_and_league,go_kart_club] +gymnastics_club; [active_life,sports_club_and_league,gymnastics_club] +lawn_bowling_club; [active_life,sports_club_and_league,lawn_bowling_club] +nudist_clubs; [active_life,sports_club_and_league,nudist_clubs] +paddle_tennis_club; [active_life,sports_club_and_league,paddle_tennis_club] +professional_sports_league; [active_life,sports_club_and_league,professional_sports_league] +professional_sports_team; [active_life,sports_club_and_league,professional_sports_team] +rowing_club; [active_life,sports_club_and_league,rowing_club] +sailing_club; [active_life,sports_club_and_league,sailing_club] +school_sports_league; [active_life,sports_club_and_league,school_sports_league] +school_sports_team; [active_life,sports_club_and_league,school_sports_team] +soccer_club; [active_life,sports_club_and_league,soccer_club] +surf_lifesaving_club; [active_life,sports_club_and_league,surf_lifesaving_club] +table_tennis_club; [active_life,sports_club_and_league,table_tennis_club] +volleyball_club; [active_life,sports_club_and_league,volleyball_club] +golf_club; [active_life,sports_club_and_league,golf_club] +indoor_golf_center; [active_life,sports_club_and_league,golf_club,indoor_golf_center] +martial_arts_club; [active_life,sports_club_and_league,martial_arts_club] +brazilian_jiu_jitsu_club; [active_life,sports_club_and_league,martial_arts_club,brazilian_jiu_jitsu_club] +chinese_martial_arts_club; [active_life,sports_club_and_league,martial_arts_club,chinese_martial_arts_club] +karate_club; [active_life,sports_club_and_league,martial_arts_club,karate_club] +kickboxing_club; [active_life,sports_club_and_league,martial_arts_club,kickboxing_club] +muay_thai_club; [active_life,sports_club_and_league,martial_arts_club,muay_thai_club] +taekwondo_club; [active_life,sports_club_and_league,martial_arts_club,taekwondo_club] +sports_and_recreation_rental_and_services; [active_life,sports_and_recreation_rental_and_services] +beach_equipment_rentals; [active_life,sports_and_recreation_rental_and_services,beach_equipment_rentals] +bike_rentals; [active_life,sports_and_recreation_rental_and_services,bike_rentals] +canoe_and_kayak_hire_service; [active_life,sports_and_recreation_rental_and_services,boat_hire_service,canoe_and_kayak_hire_service] +scooter_rental; [active_life,sports_and_recreation_rental_and_services,scooter_rental] +sport_equipment_rentals; [active_life,sports_and_recreation_rental_and_services,sport_equipment_rentals] +beauty_and_spa; [beauty_and_spa] +aromatherapy; [beauty_and_spa,aromatherapy] +beauty_salon; [beauty_and_spa,beauty_salon] +acne_treatment; [beauty_and_spa,acne_treatment] +barber; [beauty_and_spa,barber] +eyebrow_service; [beauty_and_spa,eyebrow_service] +eyelash_service; [beauty_and_spa,eyelash_service] +foot_care; [beauty_and_spa,foot_care] +hair_extensions; [beauty_and_spa,hair_extensions] +hair_loss_center; [beauty_and_spa,hair_loss_center] +hair_replacement; [beauty_and_spa,hair_replacement] +health_spa; [beauty_and_spa,health_spa] +makeup_artist; [beauty_and_spa,makeup_artist] +massage; [beauty_and_spa,massage] +nail_salon; [beauty_and_spa,nail_salon] +onsen; [beauty_and_spa,onsen] +permanent_makeup; [beauty_and_spa,permanent_makeup] +tattoo_and_piercing; [beauty_and_spa,tattoo_and_piercing] +piercing; [beauty_and_spa,tattoo_and_piercing,piercing] +tattoo; [beauty_and_spa,tattoo_and_piercing,tattoo] +public_bath_houses; [beauty_and_spa,public_bath_houses] +teeth_whitening; [beauty_and_spa,teeth_whitening] +turkish_baths; [beauty_and_spa,turkish_baths] +hair_removal; [beauty_and_spa,hair_removal] +laser_hair_removal; [beauty_and_spa,hair_removal,laser_hair_removal] +sugaring; [beauty_and_spa,hair_removal,sugaring] +threading_service; [beauty_and_spa,hair_removal,threading_service] +waxing; [beauty_and_spa,hair_removal,waxing] +hair_salon; [beauty_and_spa,hair_salon] +blow_dry_blow_out_service; [beauty_and_spa,hair_salon,blow_dry_blow_out_service] +hair_stylist; [beauty_and_spa,hair_salon,hair_stylist] +kids_hair_salon; [beauty_and_spa,hair_salon,kids_hair_salon] +spas; [beauty_and_spa,spas] +medical_spa; [beauty_and_spa,spas,medical_spa] +day_spa; [beauty_and_spa,spas,day_spa] +skin_care; [beauty_and_spa,skin_care] +esthetician; [beauty_and_spa,skin_care,esthetician] +tanning_salon; [beauty_and_spa,tanning_salon] +spray_tanning; [beauty_and_spa,tanning_salon,spray_tanning] +tanning_bed; [beauty_and_spa,tanning_salon,tanning_bed] +image_consultant; [beauty_and_spa,image_consultant] +education; [education] +adult_education; [education,adult_education] +board_of_education_offices; [education,board_of_education_offices] +campus_building; [education,campus_building] +college_counseling; [education,college_counseling] +college_university; [education,college_university] +architecture_schools; [education,college_university,architecture_schools] +business_schools; [education,college_university,business_schools] +engineering_schools; [education,college_university,engineering_schools] +law_schools; [education,college_university,law_schools] +medical_sciences_schools; [education,college_university,medical_sciences_schools] +dentistry_schools; [education,college_university,medical_sciences_schools,dentistry_schools] +pharmacy_schools; [education,college_university,medical_sciences_schools,pharmacy_schools] +veterinary_schools; [education,college_university,medical_sciences_schools,veterinary_schools] +science_schools; [education,college_university,science_schools] +educational_research_institute; [education,educational_research_institute] +educational_services; [education,educational_services] +archaeological_services; [education,educational_services,archaeological_services] +educational_camp; [education,educational_camp] +private_tutor; [education,private_tutor] +school; [education,school] +charter_school; [education,school,charter_school] +elementary_school; [education,school,elementary_school] +high_school; [education,school,high_school] +middle_school; [education,school,middle_school] +montessori_school; [education,school,montessori_school] +preschool; [education,school,preschool] +private_school; [education,school,private_school] +public_school; [education,school,public_school] +religious_school; [education,school,religious_school] +waldorf_school; [education,school,waldorf_school] +school_district_offices; [education,school_district_offices] +specialty_school; [education,specialty_school] +art_school; [education,specialty_school,art_school] +bartending_school; [education,specialty_school,bartending_school] +cheerleading; [education,specialty_school,cheerleading] +childbirth_education; [education,specialty_school,childbirth_education] +circus_school; [education,specialty_school,circus_school] +computer_coaching; [education,specialty_school,computer_coaching] +cooking_school; [education,specialty_school,cooking_school] +cosmetology_school; [education,specialty_school,cosmetology_school] +cpr_classes; [education,specialty_school,cpr_classes] +drama_school; [education,specialty_school,drama_school] +driving_school; [education,specialty_school,driving_school] +dui_school; [education,specialty_school,dui_school] +firearm_training; [education,specialty_school,firearm_training] +first_aid_class; [education,specialty_school,first_aid_class] +flight_school; [education,specialty_school,flight_school] +food_safety_training; [education,specialty_school,food_safety_training] +language_school; [education,specialty_school,language_school] +massage_school; [education,specialty_school,massage_school] +medical_school; [education,specialty_school,medical_school] +music_school; [education,specialty_school,music_school] +nursing_school; [education,specialty_school,nursing_school] +parenting_classes; [education,specialty_school,parenting_classes] +photography_classes; [education,specialty_school,photography_classes] +speech_training; [education,specialty_school,speech_training] +sports_school; [education,specialty_school,sports_school] +traffic_school; [education,specialty_school,traffic_school] +vocational_and_technical_school; [education,specialty_school,vocational_and_technical_school] +student_union; [education,student_union] +tasting_classes; [education,tasting_classes] +cheese_tasting_classes; [education,tasting_classes,cheese_tasting_classes] +wine_tasting_classes; [education,tasting_classes,wine_tasting_classes] +test_preparation; [education,test_preparation] +tutoring_center; [education,tutoring_center] +civil_examinations_academy; [education,tutoring_center,civil_examinations_academy] +financial_service; [financial_service] +accountant; [financial_service,accountant] +atms; [financial_service,atms] +bank_credit_union; [financial_service,bank_credit_union] +banks; [financial_service,bank_credit_union,banks] +credit_union; [financial_service,bank_credit_union,credit_union] +brokers; [financial_service,brokers] +business_brokers; [financial_service,brokers,business_brokers] +stock_and_bond_brokers; [financial_service,brokers,stock_and_bond_brokers] +business_banking_service; [financial_service,business_banking_service] +business_financing; [financial_service,business_financing] +check_cashing_payday_loans; [financial_service,check_cashing_payday_loans] +coin_dealers; [financial_service,coin_dealers] +collection_agencies; [financial_service,collection_agencies] +credit_and_debt_counseling; [financial_service,credit_and_debt_counseling] +currency_exchange; [financial_service,currency_exchange] +debt_relief_services; [financial_service,debt_relief_services] +financial_advising; [financial_service,financial_advising] +holding_companies; [financial_service,holding_companies] +investment_management_company; [financial_service,investment_management_company] +installment_loans; [financial_service,installment_loans] +mortgage_lender; [financial_service,installment_loans,mortgage_lender] +auto_loan_provider; [financial_service,installment_loans,auto_loan_provider] +insurance_agency; [financial_service,insurance_agency] +auto_insurance; [financial_service,insurance_agency,auto_insurance] +farm_insurance; [financial_service,insurance_agency,farm_insurance] +fidelity_and_surety_bonds; [financial_service,insurance_agency,fidelity_and_surety_bonds] +home_and_rental_insurance; [financial_service,insurance_agency,home_and_rental_insurance] +life_insurance; [financial_service,insurance_agency,life_insurance] +investing; [financial_service,investing] +money_transfer_services; [financial_service,money_transfer_services] +tax_services; [financial_service,tax_services] +trusts; [financial_service,trusts] +private_establishments_and_corporates; [private_establishments_and_corporates] +corporate_entertainment_services; [private_establishments_and_corporates,corporate_entertainment_services] +corporate_gift_supplier; [private_establishments_and_corporates,corporate_gift_supplier] +corporate_office; [private_establishments_and_corporates,corporate_office] +private_equity_firm; [private_establishments_and_corporates,private_equity_firm] +retail; [retail] +food; [retail,food] +back_shop; [retail,food,back_shop] +bagel_shop; [retail,food,bagel_shop] +bakery; [retail,food,bakery] +flatbread; [retail,food,bakery,flatbread] +beer_wine_and_spirits; [retail,food,beer_wine_and_spirits] +box_lunch_supplier; [retail,food,box_lunch_supplier] +patisserie_cake_shop; [retail,food,patisserie_cake_shop] +chimney_cake_shop; [retail,food,patisserie_cake_shop,chimney_cake_shop] +cupcake_shop; [retail,food,patisserie_cake_shop,cupcake_shop] +custom_cakes_shop; [retail,food,patisserie_cake_shop,custom_cakes_shop] +coffee_and_tea_supplies; [retail,food,coffee_and_tea_supplies] +csa_farm; [retail,food,csa_farm] +desserts; [retail,food,desserts] +donuts; [retail,food,donuts] +fishmonger; [retail,food,fishmonger] +food_delivery_service; [retail,food,food_delivery_service] +food_stand; [retail,food,food_stand] +food_truck; [retail,food,food_truck] +friterie; [retail,food,friterie] +health_food_store; [retail,food,health_food_store] +ice_cream_and_frozen_yoghurt; [retail,food,ice_cream_and_frozen_yoghurt] +gelato; [retail,food,ice_cream_and_frozen_yoghurt,gelato] +ice_cream_shop; [retail,food,ice_cream_and_frozen_yoghurt,ice_cream_shop] +frozen_yoghurt_shop; [retail,food,ice_cream_and_frozen_yoghurt,frozen_yoghurt_shop] +shaved_ice_shop; [retail,food,ice_cream_and_frozen_yoghurt,shaved_ice_shop] +shaved_snow_shop; [retail,food,ice_cream_and_frozen_yoghurt,shaved_snow_shop] +imported_food; [retail,food,imported_food] +kiosk; [retail,food,kiosk] +liquor_store; [retail,food,liquor_store] +mulled_wine; [retail,food,mulled_wine] +pie_shop; [retail,food,pie_shop] +pretzels; [retail,food,pretzels] +sandwich_shop; [retail,food,sandwich_shop] +smokehouse; [retail,food,smokehouse] +street_vendor; [retail,food,street_vendor] +pizza_delivery_service; [retail,food,food_delivery_service,pizza_delivery_service] +specialty_foods; [retail,food,specialty_foods] +delicatessen; [retail,food,specialty_foods,delicatessen] +frozen_foods; [retail,food,specialty_foods,frozen_foods] +indian_sweets_shop; [retail,food,specialty_foods,indian_sweets_shop] +macarons; [retail,food,specialty_foods,macarons] +pasta_shop; [retail,food,specialty_foods,pasta_shop] +winery; [retail,food,winery] +wine_tasting_room; [retail,food,winery,wine_tasting_room] +auto_parts_and_supply_store; [retail,auto_parts_and_supply_store] +beverage_store; [retail,beverage_store] +boat_parts_and_supply_store; [retail,boat_parts_and_supply_store] +butcher_shop; [retail,food,butcher_shop] +candy_store; [retail,food,candy_store] +japanese_confectionery_shop; [retail,food,candy_store,japanese_confectionery_shop] +carpet_store; [retail,carpet_store] +cheese_shop; [retail,food,cheese_shop] +chocolatier; [retail,food,chocolatier] +distillery; [retail,distillery] +drugstore; [retail,drugstore] +flooring_store; [retail,flooring_store] +fruits_and_vegetables; [retail,food,fruits_and_vegetables] +health_market; [retail,health_market] +hearing_aid_provider; [retail,hearing_aid_provider] +herb_and_spice_shop; [retail,herb_and_spice_shop] +honey_farm_shop; [retail,honey_farm_shop] +meat_shop; [retail,meat_shop] +olive_oil; [retail,olive_oil] +party_supply; [retail,party_supply] +pharmacy; [retail,pharmacy] +popcorn_shop; [retail,popcorn_shop] +seafood_market; [retail,seafood_market] +water_store; [retail,water_store] +shopping; [retail,shopping] +arts_and_crafts; [retail,shopping,arts_and_crafts] +art_supply_store; [retail,shopping,arts_and_crafts,art_supply_store] +atelier; [retail,shopping,arts_and_crafts,atelier] +cooking_classes; [retail,shopping,arts_and_crafts,cooking_classes] +costume_store; [retail,shopping,arts_and_crafts,costume_store] +craft_shop; [retail,shopping,arts_and_crafts,craft_shop] +embroidery_and_crochet; [retail,shopping,arts_and_crafts,embroidery_and_crochet] +fabric_store; [retail,shopping,arts_and_crafts,fabric_store] +framing_store; [retail,shopping,arts_and_crafts,framing_store] +handicraft_shop; [retail,shopping,arts_and_crafts,handicraft_shop] +paint_your_own_pottery; [retail,shopping,arts_and_crafts,paint_your_own_pottery] +books_mags_music_and_video; [retail,shopping,books_mags_music_and_video] +bookstore; [retail,shopping,books_mags_music_and_video,bookstore] +academic_bookstore; [retail,shopping,books_mags_music_and_video,academic_bookstore] +comic_books_store; [retail,shopping,books_mags_music_and_video,comic_books_store] +music_and_dvd_store; [retail,shopping,books_mags_music_and_video,music_and_dvd_store] +newspaper_and_magazines_store; [retail,shopping,books_mags_music_and_video,newspaper_and_magazines_store] +video_and_video_game_rentals; [retail,shopping,books_mags_music_and_video,video_and_video_game_rentals] +video_game_store; [retail,shopping,books_mags_music_and_video,video_game_store] +vinyl_record_store; [retail,shopping,books_mags_music_and_video,vinyl_record_store] +building_supply_store; [retail,shopping,building_supply_store] +lumber_store; [retail,shopping,building_supply_store,lumber_store] +fashion; [retail,shopping,fashion] +clothing_store; [retail,shopping,fashion,clothing_store] +ceremonial_clothing; [retail,shopping,fashion,clothing_store,ceremonial_clothing] +children's_clothing_store; [retail,shopping,fashion,clothing_store,children's_clothing_store] +clothing_rental; [retail,shopping,fashion,clothing_store,clothing_rental] +denim_wear_store; [retail,shopping,fashion,clothing_store,denim_wear_store] +designer_clothing; [retail,shopping,fashion,clothing_store,designer_clothing] +formal_wear_store; [retail,shopping,fashion,clothing_store,formal_wear_store] +fur_clothing; [retail,shopping,fashion,clothing_store,fur_clothing] +lingerie_store; [retail,shopping,fashion,clothing_store,lingerie_store] +maternity_wear; [retail,shopping,fashion,clothing_store,maternity_wear] +men's_clothing_store; [retail,shopping,fashion,clothing_store,men's_clothing_store] +t_shirt_store; [retail,shopping,fashion,clothing_store,t_shirt_store] +custom_t_shirt_store; [retail,shopping,fashion,clothing_store,t_shirt_store] +traditional_clothing; [retail,shopping,fashion,clothing_store,traditional_clothing] +women's_clothing_store; [retail,shopping,fashion,clothing_store,women's_clothing_store] +fashion_accessories_store; [retail,shopping,fashion,fashion_accessories_store] +hat_shop; [retail,shopping,fashion,hat_shop] +leather_goods; [retail,shopping,fashion,leather_goods] +plus_size_clothing; [retail,shopping,fashion,plus_size_clothing] +saree_shop; [retail,shopping,fashion,saree_shop] +sleepwear; [retail,shopping,fashion,sleepwear] +stocking; [retail,shopping,fashion,stocking] +used_vintage_and_consignment; [retail,shopping,fashion,used_vintage_and_consignment] +handbag_stores; [retail,shopping,fashion,fashion_accessories_store,handbag_stores] +shoe_store; [retail,shopping,fashion,shoe_store] +orthopedic_shoe_store; [retail,shopping,fashion,shoe_store,orthopedic_shoe_store] +cosmetic_and_beauty_supplies; [retail,shopping,cosmetic_and_beauty_supplies] +hair_supply_stores; [retail,shopping,cosmetic_and_beauty_supplies,hair_supply_stores] +eyewear_and_optician; [retail,shopping,eyewear_and_optician] +sunglasses_store; [retail,shopping,eyewear_and_optician,sunglasses_store] +farming_equipment_store; [retail,shopping,farming_equipment_store] +forklift_dealer; [retail,shopping,farming_equipment_store,forklift_dealer] +flowers_and_gifts_shop; [retail,shopping,flowers_and_gifts_shop] +florist; [retail,shopping,flowers_and_gifts_shop,florist] +gift_shop; [retail,shopping,flowers_and_gifts_shop,gift_shop] +grocery_store; [retail,shopping,grocery_store] +specialty_grocery_store; [retail,shopping,grocery_store,specialty_grocery_store] +asian_grocery_store; [retail,shopping,grocery_store,asian_grocery_store] +dairy_stores; [retail,shopping,grocery_store,dairy_stores] +ethical_grocery; [retail,shopping,grocery_store,ethical_grocery] +indian_grocery_store; [retail,shopping,grocery_store,indian_grocery_store] +japanese_grocery_store; [retail,shopping,grocery_store,japanese_grocery_store] +korean_grocery_store; [retail,shopping,grocery_store,korean_grocery_store] +kosher_grocery_store; [retail,shopping,grocery_store,kosher_grocery_store] +mexican_grocery_store; [retail,shopping,grocery_store,mexican_grocery_store] +organic_grocery_store; [retail,shopping,grocery_store,organic_grocery_store] +rice_shop; [retail,shopping,grocery_store,rice_shop] +russian_grocery_store; [retail,shopping,grocery_store,russian_grocery_store] +home_and_garden; [retail,shopping,home_and_garden] +hardware_store; [retail,shopping,home_and_garden,hardware_store] +welding_supply_store; [retail,shopping,home_and_garden,hardware_store,welding_supply_store] +appliance_store; [retail,shopping,home_and_garden,appliance_store] +bedding_and_bath_stores; [retail,shopping,home_and_garden,bedding_and_bath_stores] +candle_store; [retail,shopping,home_and_garden,candle_store] +christmas_trees; [retail,shopping,home_and_garden,christmas_trees] +electrical_supply_store; [retail,shopping,home_and_garden,electrical_supply_store] +furniture_accessory_store; [retail,shopping,home_and_garden,furniture_accessory_store] +furniture_store; [retail,shopping,home_and_garden,furniture_store] +grilling_equipment; [retail,shopping,home_and_garden,grilling_equipment] +holiday_decor; [retail,shopping,home_and_garden,holiday_decor] +home_decor; [retail,shopping,home_and_garden,home_decor] +home_goods_store; [retail,shopping,home_and_garden,home_goods_store] +home_improvement_store; [retail,shopping,home_and_garden,home_improvement_store] +hot_tubs_and_pools; [retail,shopping,home_and_garden,hot_tubs_and_pools] +lawn_mower_store; [retail,shopping,home_and_garden,lawn_mower_store] +lighting_store; [retail,shopping,home_and_garden,lighting_store] +linen; [retail,shopping,home_and_garden,linen] +mattress_store; [retail,shopping,home_and_garden,mattress_store] +nursery_and_gardening; [retail,shopping,home_and_garden,nursery_and_gardening] +outdoor_furniture_store; [retail,shopping,home_and_garden,outdoor_furniture_store] +paint_store; [retail,shopping,home_and_garden,paint_store] +playground_equipment_supplier; [retail,shopping,home_and_garden,playground_equipment_supplier_] +pumpkin_patch; [retail,shopping,home_and_garden,pumpkin_patch] +rug_store; [retail,shopping,home_and_garden,rug_store] +tableware_supplier; [retail,shopping,home_and_garden,tableware_supplier] +tile_store; [retail,shopping,home_and_garden,tile_store] +wallpaper_store; [retail,shopping,home_and_garden,wallpaper_store] +window_treatment_store; [retail,shopping,home_and_garden,window_treatment_store] +woodworking_supply_store; [retail,shopping,home_and_garden,woodworking_supply_store] +kitchen_and_bath; [retail,shopping,home_and_garden,kitchen_and_bath] +bathroom_fixture_stores; [retail,shopping,home_and_garden,kitchen_and_bath,bathroom_fixture_stores] +kitchen_supply_store; [retail,shopping,home_and_garden,kitchen_and_bath,kitchen_supply_store] +hydroponic_gardening; [retail,shopping,home_and_garden,nursery_and_gardening,hydroponic_gardening] +medical_supply; [retail,shopping,medical_supply] +dental_supply_store; [retail,shopping,medical_supply,dental_supply_store] +hearing_aids; [retail,shopping,medical_supply,hearing_aids] +pet_store; [retail,shopping,pet_store] +bird_shop; [retail,shopping,pet_store,bird_shop] +aquatic_pet_store; [retail,shopping,pet_store,aquatic_pet_store] +reptile_shop; [retail,shopping,pet_store,reptile_shop] +adult_store; [retail,shopping,adult_store] +agricultural_seed_store; [retail,shopping,agricultural_seed_store] +antique_store; [retail,shopping,antique_store] +army_and_navy_store; [retail,shopping,army_and_navy_store] +auction_house; [retail,shopping,auction_house] +car_auction; [retail,shopping,auction_house,car_auction] +baby_gear_and_furniture; [retail,shopping,baby_gear_and_furniture] +battery_store; [retail,shopping,battery_store] +bazaars; [retail,shopping,bazaars] +boutique; [retail,shopping,boutique] +brewing_supply_store; [retail,shopping,brewing_supply_store] +bridal_shop; [retail,shopping,bridal_shop] +cannabis_dispensary; [retail,shopping,cannabis_dispensary] +cards_and_stationery_store; [retail,shopping,cards_and_stationery_store] +computer_store; [retail,shopping,computer_store] +concept_shop; [retail,shopping,concept_shop] +convenience_store; [retail,shopping,convenience_store] +custom_clothing; [retail,shopping,custom_clothing] +customized_merchandise; [retail,shopping,customized_merchandise] +department_store; [retail,shopping,department_store] +discount_store; [retail,shopping,discount_store] +do_it_yourself_store; [retail,shopping,do_it_yourself_store] +drone_store; [retail,shopping,drone_store] +duty_free_shop; [retail,shopping,duty_free_shop] +educational_supply_store; [retail,shopping,educational_supply_store] +electronics; [retail,shopping,electronics] +farmers_market; [retail,shopping,farmers_market] +firework_retailer; [retail,shopping,firework_retailer] +fitness_exercise_equipment; [retail,shopping,fitness_exercise_equipment] +flea_market; [retail,shopping,flea_market] +flower_markets; [retail,shopping,flower_markets] +gemstone_and_mineral; [retail,shopping,gemstone_and_mineral] +gold_buyer; [retail,shopping,gold_buyer] +guitar_store; [retail,shopping,guitar_store] +gun_and_ammo; [retail,shopping,gun_and_ammo] +herbal_shop; [retail,shopping,herbal_shop] +audio_visual_equipment_store; [retail,shopping,audio_visual_equipment_store] +hobby_shop; [retail,shopping,hobby_shop] +home_theater_systems_stores; [retail,shopping,home_theater_systems_stores] +horse_equipment_shop; [retail,shopping,horse_equipment_shop] +international_grocery_store; [retail,shopping,international_grocery_store] +jewelry_store; [retail,shopping,jewelry_store] +knitting_supply; [retail,shopping,knitting_supply] +livestock_feed_and_supply_store; [retail,shopping,livestock_feed_and_supply_store] +luggage_store; [retail,shopping,luggage_store] +market_stall; [retail,shopping,market_stall] +military_surplus_store; [retail,shopping,military_surplus_store] +mobile_phone_accessories; [retail,shopping,mobile_phone_accessories] +mobile_phone_store; [retail,shopping,mobile_phone_store] +musical_instrument_store; [retail,shopping,musical_instrument_store] +night_market; [retail,shopping,night_market] +office_equipment; [retail,shopping,office_equipment] +online_shop; [retail,shopping,online_shop] +outlet_store; [retail,shopping,outlet_store] +packing_supply; [retail,shopping,packing_supply] +pawn_shop; [retail,shopping,pawn_shop] +pen_store; [retail,shopping,pen_store] +perfume_store; [retail,shopping,perfume_store] +personal_shopper; [retail,shopping,personal_shopper] +photography_store_and_services; [retail,shopping,photography_store_and_services] +piano_store; [retail,shopping,piano_store] +pool_and_billiards; [retail,shopping,pool_and_billiards] +pop_up_shop; [retail,shopping,pop_up_shop] +props; [retail,shopping,props] +public_market; [retail,shopping,public_market] +religious_items; [retail,shopping,religious_items] +rental_kiosks; [retail,shopping,rental_kiosks] +safe_store; [retail,shopping,safe_store] +safety_equipment; [retail,shopping,safety_equipment] +shopping_center; [retail,shopping,shopping_center] +shopping_passage; [retail,shopping,shopping_passage] +souvenir_shop; [retail,shopping,souvenir_shop] +spiritual_shop; [retail,shopping,spiritual_shop] +supermarket; [retail,shopping,supermarket] +superstore; [retail,shopping,superstore] +tabletop_games; [retail,shopping,tabletop_games] +thrift_store; [retail,shopping,thrift_store] +tobacco_shop; [retail,shopping,tobacco_shop] +toy_store; [retail,shopping,toy_store] +trophy_shop; [retail,shopping,trophy_shop] +uniform_store; [retail,shopping,uniform_store] +used_bookstore; [retail,shopping,used_bookstore] +e_cigarette_store; [retail,shopping,e_cigarette_store] +vitamins_and_supplements; [retail,shopping,vitamins_and_supplements] +watch_store; [retail,shopping,watch_store] +wholesale_store; [retail,shopping,wholesale_store] +wig_store; [retail,shopping,wig_store] +sporting_goods; [retail,shopping,sporting_goods] +archery_shop; [retail,shopping,sporting_goods,archery_shop] +bicycle_shop; [retail,shopping,sporting_goods,bicycle_shop] +dive_shop; [retail,shopping,sporting_goods,dive_shop] +golf_equipment; [retail,shopping,sporting_goods,golf_equipment] +hockey_equipment; [retail,shopping,sporting_goods,hockey_equipment] +hunting_and_fishing_supplies; [retail,shopping,sporting_goods,hunting_and_fishing_supplies] +outdoor_gear; [retail,shopping,sporting_goods,outdoor_gear] +skate_shop; [retail,shopping,sporting_goods,skate_shop] +ski_and_snowboard_shop; [retail,shopping,sporting_goods,ski_and_snowboard_shop] +sports_wear; [retail,shopping,sporting_goods,sports_wear] +dance_wear; [retail,shopping,sporting_goods,sports_wear,dance_wear] +surf_shop; [retail,shopping,sporting_goods,surf_shop] +swimwear_store; [retail,shopping,sporting_goods,swimwear_store] +tire_shop; [retail,tire_shop] +tire_repair_shop; [retail,tire_shop,tire_repair_shop] +health_and_medical; [health_and_medical] +abuse_and_addiction_treatment; [health_and_medical,abuse_and_addiction_treatment] +alcohol_and_drug_treatment_centers; [health_and_medical,abuse_and_addiction_treatment,alcohol_and_drug_treatment_centers] +crisis_intervention_services; [health_and_medical,abuse_and_addiction_treatment,crisis_intervention_services] +eating_disorder_treatment_centers; [health_and_medical,abuse_and_addiction_treatment,eating_disorder_treatment_centers] +counseling_and_mental_health; [health_and_medical,counseling_and_mental_health] +family_counselor; [health_and_medical,counseling_and_mental_health,family_counselor] +marriage_or_relationship_counselor; [health_and_medical,counseling_and_mental_health,marriage_or_relationship_counselor] +psychoanalyst; [health_and_medical,counseling_and_mental_health,psychoanalyst] +psychologist; [health_and_medical,counseling_and_mental_health,psychologist] +psychotherapist; [health_and_medical,counseling_and_mental_health,psychotherapist] +sex_therapist; [health_and_medical,counseling_and_mental_health,sex_therapist] +sophrologist; [health_and_medical,counseling_and_mental_health,sophrologist] +sports_psychologist; [health_and_medical,counseling_and_mental_health,sports_psychologist] +stress_management_services; [health_and_medical,counseling_and_mental_health,stress_management_services] +suicide_prevention_services; [health_and_medical,counseling_and_mental_health,suicide_prevention_services] +dental_hygienist; [health_and_medical,dental_hygienist] +mobile_clinic; [health_and_medical,dental_hygienist,mobile_clinic] +storefront_clinic; [health_and_medical,dental_hygienist,storefront_clinic] +dentist; [health_and_medical,dentist] +cosmetic_dentist; [health_and_medical,dentist,cosmetic_dentist] +endodontist; [health_and_medical,dentist,endodontist] +general_dentistry; [health_and_medical,dentist,general_dentistry] +oral_surgeon; [health_and_medical,dentist,oral_surgeon] +orthodontist; [health_and_medical,dentist,orthodontist] +pediatric_dentist; [health_and_medical,dentist,pediatric_dentist] +periodontist; [health_and_medical,dentist,periodontist] +diagnostic_services; [health_and_medical,diagnostic_services] +diagnostic_imaging; [health_and_medical,diagnostic_services,diagnostic_imaging] +laboratory_testing; [health_and_medical,diagnostic_services,laboratory_testing] +doctor; [health_and_medical,doctor] +allergist; [health_and_medical,doctor,allergist] +anesthesiologist; [health_and_medical,doctor,anesthesiologist] +audiologist; [health_and_medical,doctor,audiologist] +cardiologist; [health_and_medical,doctor,cardiologist] +cosmetic_surgeon; [health_and_medical,doctor,cosmetic_surgeon] +dermatologist; [health_and_medical,doctor,dermatologist] +ear_nose_and_throat; [health_and_medical,doctor,ear_nose_and_throat] +emergency_medicine; [health_and_medical,doctor,emergency_medicine] +endocrinologist; [health_and_medical,doctor,endocrinologist] +endoscopist; [health_and_medical,doctor,endoscopist] +family_practice; [health_and_medical,doctor,family_practice] +fertility; [health_and_medical,doctor,fertility] +gastroenterologist; [health_and_medical,doctor,gastroenterologist] +geneticist; [health_and_medical,doctor,geneticist] +gerontologist; [health_and_medical,doctor,gerontologist] +hepatologist; [health_and_medical,doctor,hepatologist] +homeopathic_medicine; [health_and_medical,doctor,homeopathic_medicine] +hospitalist; [health_and_medical,doctor,hospitalist] +immunodermatologist; [health_and_medical,doctor,immunodermatologist] +infectious_disease_specialist; [health_and_medical,doctor,infectious_disease_specialist] +naturopathic_holistic; [health_and_medical,doctor,naturopathic_holistic] +nephrologist; [health_and_medical,doctor,nephrologist] +neurologist; [health_and_medical,doctor,neurotologist] +neurotologist; [health_and_medical,doctor,neurologist] +neuropathologist; [health_and_medical,doctor,neuropathologist] +obstetrician_and_gynecologist; [health_and_medical,doctor,obstetrician_and_gynecologist] +oncologist; [health_and_medical,doctor,oncologist] +orthopedist; [health_and_medical,doctor,orthopedist] +osteopathic_physician; [health_and_medical,doctor,osteopathic_physician] +otologist; [health_and_medical,doctor,otologist] +pain_management; [health_and_medical,doctor,pain_management] +pathologist; [health_and_medical,doctor,pathologist] +phlebologist; [health_and_medical,doctor,phlebologist] +physician_assistant; [health_and_medical,doctor,physician_assistant] +plastic_surgeon; [health_and_medical,doctor,plastic_surgeon] +podiatrist; [health_and_medical,doctor,podiatrist] +preventive_medicine; [health_and_medical,doctor,preventive_medicine] +proctologist; [health_and_medical,doctor,proctologist] +pulmonologist; [health_and_medical,doctor,pulmonologist] +radiologist; [health_and_medical,doctor,radiologist] +rheumatologist; [health_and_medical,doctor,rheumatologist] +spine_surgeon; [health_and_medical,doctor,spine_surgeon] +sports_medicine; [health_and_medical,doctor,sports_medicine] +tattoo_removal; [health_and_medical,doctor,tattoo_removal] +toxicologist; [health_and_medical,doctor,toxicologist] +tropical_medicine; [health_and_medical,doctor,tropical_medicine] +undersea_hyperbaric_medicine; [health_and_medical,doctor,undersea_hyperbaric_medicine] +urologist; [health_and_medical,doctor,urologist] +vascular_medicine; [health_and_medical,doctor,vascular_medicine] +geriatric_medicine; [health_and_medical,doctor,geriatric_medicine] +geriatric_psychiatry; [health_and_medical,doctor,geriatric_medicine,geriatric_psychiatry] +abortion_clinic; [health_and_medical,abortion_clinic] +acupuncture; [health_and_medical,acupuncture] +aesthetician; [health_and_medical,aesthetician] +alcohol_and_drug_treatment_center; [health_and_medical,alcohol_and_drug_treatment_center] +alternative_medicine; [health_and_medical,alternative_medicine] +ambulance_and_ems_services; [health_and_medical,ambulance_and_ems_services] +animal_assisted_therapy; [health_and_medical,animal_assisted_therapy] +assisted_living_facility; [health_and_medical,assisted_living_facility] +ayurveda; [health_and_medical,ayurveda] +behavior_analyst; [health_and_medical,behavior_analyst] +blood_and_plasma_donation_center; [health_and_medical,blood_and_plasma_donation_center] +body_contouring; [health_and_medical,body_contouring] +cancer_treatment_center; [health_and_medical,cancer_treatment_center] +cannabis_clinic; [health_and_medical,cannabis_clinic] +cannabis_collective; [health_and_medical,cannabis_collective] +childrens_hospital; [health_and_medical,childrens_hospital] +chiropractor; [health_and_medical,chiropractor] +colonics; [health_and_medical,colonics] +community_health_center; [health_and_medical,community_health_center] +concierge_medicine; [health_and_medical,concierge_medicine] +cryotherapy; [health_and_medical,cryotherapy] +dialysis_clinic; [health_and_medical,dialysis_clinic] +dietitian; [health_and_medical,dietitian] +doula; [health_and_medical,doula] +emergency_room; [health_and_medical,emergency_room] +environmental_medicine; [health_and_medical,environmental_medicine] +eye_care_clinic; [health_and_medical,eye_care_clinic] +float_spa; [health_and_medical,float_spa] +halfway_house; [health_and_medical,halfway_house] +halotherapy; [health_and_medical,halotherapy] +health_and_wellness_club; [health_and_medical,health_and_wellness_club] +health_coach; [health_and_medical,health_coach] +health_department; [health_and_medical,health_department] +health_insurance_office; [health_and_medical,health_insurance_office] +hospice; [health_and_medical,hospice] +hospital; [health_and_medical,hospital] +hydrotherapy; [health_and_medical,hydrotherapy] +hypnosis_hypnotherapy; [health_and_medical,hypnosis_hypnotherapy] +iv_hydration; [health_and_medical,iv_hydration] +lactation_services; [health_and_medical,lactation_services] +laser_eye_surgery_lasik; [health_and_medical,laser_eye_surgery_lasik] +lice_treatment; [health_and_medical,lice_treatment] +massage_therapy; [health_and_medical,massage_therapy] +maternity_centers; [health_and_medical,maternity_centers] +medical_cannabis_referral; [health_and_medical,medical_cannabis_referral] +medical_service_organizations; [health_and_medical,medical_service_organizations] +medical_transportation; [health_and_medical,medical_transportation] +memory_care; [health_and_medical,memory_care] +midwife; [health_and_medical,midwife] +nurse_practitioner; [health_and_medical,nurse_practitioner] +nutritionist; [health_and_medical,nutritionist] +occupational_medicine; [health_and_medical,occupational_medicine] +occupational_therapy; [health_and_medical,occupational_therapy] +optometrist; [health_and_medical,optometrist] +organ_and_tissue_donor_service; [health_and_medical,organ_and_tissue_donor_service] +orthotics; [health_and_medical,orthotics] +oxygen_bar; [health_and_medical,oxygen_bar] +paternity_tests_and_services; [health_and_medical,paternity_tests_and_services] +physical_therapy; [health_and_medical,physical_therapy] +placenta_encapsulation_service; [health_and_medical,placenta_encapsulation_service] +podiatry; [health_and_medical,podiatry] +prenatal_perinatal_care; [health_and_medical,prenatal_perinatal_care] +prosthetics; [health_and_medical,prosthetics] +prosthodontist; [health_and_medical,prosthodontist] +psychomotor_therapist; [health_and_medical,psychomotor_therapist] +public_health_clinic; [health_and_medical,public_health_clinic] +reflexology; [health_and_medical,reflexology] +reiki; [health_and_medical,reiki] +sauna; [health_and_medical,sauna] +skilled_nursing; [health_and_medical,skilled_nursing] +sleep_specialist; [health_and_medical,sleep_specialist] +speech_therapist; [health_and_medical,speech_therapist] +sperm_clinic; [health_and_medical,sperm_clinic] +surgical_center; [health_and_medical,surgical_center] +ultrasound_imaging_center; [health_and_medical,ultrasound_imaging_center] +urgent_care_clinic; [health_and_medical,urgent_care_clinic] +weight_loss_center; [health_and_medical,weight_loss_center] +wellness_program; [health_and_medical,wellness_program] +women's_health_clinic; [health_and_medical,women's_health_clinic] +internal_medicine; [health_and_medical,doctor,internal_medicine] +hematology; [health_and_medical,doctor,internal_medicine,hematology] +medical_center; [health_and_medical,medical_center] +bulk_billing; [health_and_medical,medical_center,bulk_billing] +osteopath; [health_and_medical,medical_center,osteopath] +walk_in_clinic; [health_and_medical,medical_center,walk_in_clinic] +ophthalmologist; [health_and_medical,doctor,ophthalmologist] +retina_specialist; [health_and_medical,doctor,ophthalmologist,retina_specialist] +pediatrician; [health_and_medical,doctor,pediatrician] +pediatric_anesthesiology; [health_and_medical,doctor,pediatrician,pediatric_anesthesiology] +pediatric_cardiology; [health_and_medical,doctor,pediatrician,pediatric_cardiology] +pediatric_endocrinology; [health_and_medical,doctor,pediatrician,pediatric_endocrinology] +pediatric_gastroenterology; [health_and_medical,doctor,pediatrician,pediatric_gastroenterology] +pediatric_infectious_disease; [health_and_medical,doctor,pediatrician,pediatric_infectious_disease] +pediatric_nephrology; [health_and_medical,doctor,pediatrician,pediatric_nephrology] +pediatric_neurology; [health_and_medical,doctor,pediatrician,pediatric_neurology] +pediatric_oncology; [health_and_medical,doctor,pediatrician,pediatric_oncology] +pediatric_orthopedic_surgery; [health_and_medical,doctor,pediatrician,pediatric_orthopedic_surgery] +pediatric_pulmonology; [health_and_medical,doctor,pediatrician,pediatric_pulmonology] +pediatric_radiology; [health_and_medical,doctor,pediatrician,pediatric_radiology] +pediatric_surgery; [health_and_medical,doctor,pediatrician,pediatric_surgery] +personal_care_service; [health_and_medical,personal_care_service] +home_health_care; [health_and_medical,personal_care_service,home_health_care] +psychiatrist; [health_and_medical,doctor,psychiatrist] +child_psychiatrist; [health_and_medical,doctor,psychiatrist,child_psychiatrist] +rehabilitation_center; [health_and_medical,rehabilitation_center] +addiction_rehabilitation_center; [health_and_medical,rehabilitation_center,addiction_rehabilitation_center] +surgeon; [health_and_medical,doctor,surgeon] +cardiovascular_and_thoracic_surgeon; [health_and_medical,doctor,surgeon,cardiovascular_and_thoracic_surgeon] +traditional_chinese_medicine; [health_and_medical,traditional_chinese_medicine] +tui_na; [health_and_medical,traditional_chinese_medicine,tui_na] +pets; [pets] +pet_services; [pets,pet_services] +animal_hospital; [pets,pet_services,animal_hospital] +animal_physical_therapy; [pets,pet_services,animal_physical_therapy] +aquarium_services; [pets,pet_services,aquarium_services] +dog_walkers; [pets,pet_services,dog_walkers] +emergency_pet_hospital; [pets,pet_services,emergency_pet_hospital] +farrier_services; [pets,pet_services,farrier_services] +holistic_animal_care; [pets,pet_services,holistic_animal_care] +pet_breeder; [pets,pet_services,pet_breeder] +pet_cemetery_and_crematorium_services; [pets,pet_services,pet_cemetery_and_crematorium_services] +pet_groomer; [pets,pet_services,pet_groomer] +pet_hospice; [pets,pet_services,pet_hospice] +pet_insurance; [pets,pet_services,pet_insurance] +pet_photography; [pets,pet_services,pet_photography] +pet_sitting; [pets,pet_services,pet_sitting] +pet_transportation; [pets,pet_services,pet_transportation] +pet_waste_removal; [pets,pet_services,pet_waste_removal] +pet_boarding; [pets,pet_services,pet_sitting,pet_boarding] +pet_training; [pets,pet_services,pet_training] +dog_trainer; [pets,pet_services,pet_training,dog_trainer] +horse_trainer; [pets,pet_services,pet_training,horse_trainer] +animal_rescue_service; [pets,animal_rescue_service] +animal_shelter; [pets,animal_shelter] +horse_boarding; [pets,horse_boarding] +pet_adoption; [pets,pet_adoption] +veterinarian; [pets,veterinarian] +business_to_business; [business_to_business] +business; [business_to_business,business] +travel_company; [business_to_business,business,travel_company] +ferry_boat_company; [business_to_business,business,ferry_boat_company] +airline; [business_to_business,business,airline] +food_beverage_service_distribution; [business_to_business,business,food_beverage_service_distribution] +bottled_water_company; [business_to_business,business,bottled_water_company] +tobacco_company; [business_to_business,business,tobacco_company] +clothing_company; [business_to_business,business,clothing_company] +bags_luggage_company; [business_to_business,business,bags_luggage_company] +hotel_supply_service; [business_to_business,business,hotel_supply_service] +commercial_industrial; [business_to_business,commercial_industrial] +inventory_control_service; [business_to_business,commercial_industrial,inventory_control_service] +industrial_company; [business_to_business,commercial_industrial,industrial_company] +automation_services; [business_to_business,commercial_industrial,automation_services] +occupational_safety; [business_to_business,commercial_industrial,occupational_safety] +b2b_agriculture_and_food; [business_to_business,b2b_agriculture_and_food] +agricultural_service; [business_to_business,b2b_agriculture_and_food,agricultural_service] +agricultural_cooperatives; [business_to_business,b2b_agriculture_and_food,agricultural_cooperatives] +agriculture; [business_to_business,b2b_agriculture_and_food,agriculture] +agricultural_engineering_service; [business_to_business,b2b_agriculture_and_food,agricultural_engineering_service] +apiaries_and_beekeepers; [business_to_business,b2b_agriculture_and_food,apiaries_and_beekeepers] +b2b_dairies; [business_to_business,b2b_agriculture_and_food,b2b_dairies] +b2b_food_products; [business_to_business,b2b_agriculture_and_food,b2b_food_products] +fish_farms_and_hatcheries; [business_to_business,b2b_agriculture_and_food,fish_farms_and_hatcheries] +fish_farm; [business_to_business,b2b_agriculture_and_food,fish_farms_and_hatcheries,fish_farm] +livestock_breeder; [business_to_business,b2b_agriculture_and_food,livestock_breeder] +livestock_dealers; [business_to_business,b2b_agriculture_and_food,livestock_dealers] +poultry_farming; [business_to_business,b2b_agriculture_and_food,poultry_farming] +b2b_farming; [business_to_business,b2b_agriculture_and_food,b2b_farming] +b2b_farms; [business_to_business,b2b_agriculture_and_food,b2b_farming,b2b_farms] +pig_farm; [business_to_business,b2b_agriculture_and_food,b2b_farming,b2b_farms,pig_farm] +dairy_farm; [business_to_business,b2b_agriculture_and_food,b2b_farming,b2b_farms,dairy_farm] +urban_farm; [business_to_business,b2b_agriculture_and_food,b2b_farming,b2b_farms,urban_farm] +farming_services; [business_to_business,b2b_agriculture_and_food,b2b_farming,farming_services] +farm_equipment_and_supply; [business_to_business,b2b_agriculture_and_food,b2b_farming,farm_equipment_and_supply] +fertilizer_store; [business_to_business,b2b_agriculture_and_food,b2b_farming,farm_equipment_and_supply,fertilizer_store] +grain_elevators; [business_to_business,b2b_agriculture_and_food,b2b_farming,farm_equipment_and_supply,grain_elevators] +greenhouses; [business_to_business,b2b_agriculture_and_food,b2b_farming,farm_equipment_and_supply,greenhouses] +irrigation_companies; [business_to_business,b2b_agriculture_and_food,b2b_farming,farm_equipment_and_supply,irrigation_companies] +crops_production; [business_to_business,b2b_agriculture_and_food,crops_production] +grain_production; [business_to_business,b2b_agriculture_and_food,crops_production,grain_production] +orchards_production; [business_to_business,b2b_agriculture_and_food,crops_production,orchards_production] +business_manufacturing_and_supply; [business_to_business,business_manufacturing_and_supply] +mattress_manufacturing; [business_to_business,business_manufacturing_and_supply,mattress_manufacturing] +glass_manufacturer; [business_to_business,business_manufacturing_and_supply,glass_manufacturer] +appliance_manufacturer; [business_to_business,business_manufacturing_and_supply,appliance_manufacturer] +aircraft_manufacturer; [business_to_business,business_manufacturing_and_supply,aircraft_manufacturer] +b2b_autos_and_vehicles; [business_to_business,business_manufacturing_and_supply,b2b_autos_and_vehicles] +auto_manufacturers_and_distributors; [business_to_business,business_manufacturing_and_supply,b2b_autos_and_vehicles,auto_manufacturers_and_distributors] +b2b_tires; [business_to_business,business_manufacturing_and_supply,b2b_autos_and_vehicles,b2b_tires] +b2b_furniture_and_housewares; [business_to_business,business_manufacturing_and_supply,b2b_furniture_and_housewares] +furniture_manufacturers; [business_to_business,business_manufacturing_and_supply,b2b_furniture_and_housewares,furniture_manufacturers] +furniture_wholesalers; [business_to_business,business_manufacturing_and_supply,b2b_furniture_and_housewares,furniture_wholesalers] +b2b_machinery_and_tools; [business_to_business,business_manufacturing_and_supply,b2b_machinery_and_tools] +b2b_equipment_maintenance_and_repair; [business_to_business,business_manufacturing_and_supply,b2b_machinery_and_tools,b2b_equipment_maintenance_and_repair] +industrial_equipment; [business_to_business,business_manufacturing_and_supply,b2b_machinery_and_tools,industrial_equipment] +abrasives_supplier; [business_to_business,business_manufacturing_and_supply,abrasives_supplier] +aggregate_supplier; [business_to_business,business_manufacturing_and_supply,aggregate_supplier] +aluminum_supplier; [business_to_business,business_manufacturing_and_supply,aluminum_supplier] +b2b_apparel; [business_to_business,business_manufacturing_and_supply,b2b_apparel] +b2b_electronic_equipment; [business_to_business,business_manufacturing_and_supply,b2b_electronic_equipment] +b2b_hardware; [business_to_business,business_manufacturing_and_supply,b2b_hardware] +b2b_jewelers; [business_to_business,business_manufacturing_and_supply,b2b_jewelers] +b2b_rubber_and_plastics; [business_to_business,business_manufacturing_and_supply,b2b_rubber_and_plastics] +plastic_company; [business_to_business,business_manufacturing_and_supply,b2b_rubber_and_plastics,plastic_company] +plastic_manufacturer; [business_to_business,business_manufacturing_and_supply,b2b_rubber_and_plastics,plastic_manufacturer] +b2b_sporting_and_recreation_goods; [business_to_business,business_manufacturing_and_supply,b2b_sporting_and_recreation_goods] +b2b_textiles; [business_to_business,business_manufacturing_and_supply,b2b_textiles] +battery_inverter_supplier; [business_to_business,business_manufacturing_and_supply,battery_inverter_supplier] +bearing_supplier; [business_to_business,business_manufacturing_and_supply,bearing_supplier] +casting_molding_and_machining; [business_to_business,business_manufacturing_and_supply,casting_molding_and_machining] +cement_supplier; [business_to_business,business_manufacturing_and_supply,cement_supplier] +chemical_plant; [business_to_business,business_manufacturing_and_supply,chemical_plant] +cleaning_products_supplier; [business_to_business,business_manufacturing_and_supply,cleaning_products_supplier] +cosmetic_products_manufacturer; [business_to_business,business_manufacturing_and_supply,cosmetic_products_manufacturer] +drinking_water_dispenser; [business_to_business,business_manufacturing_and_supply,drinking_water_dispenser] +fastener_supplier; [business_to_business,business_manufacturing_and_supply,fastener_supplier] +granite_supplier; [business_to_business,business_manufacturing_and_supply,granite_supplier] +hvac_supplier; [business_to_business,business_manufacturing_and_supply,hvac_supplier] +jewelry_and_watches_manufacturer; [business_to_business,business_manufacturing_and_supply,jewelry_and_watches_manufacturer] +jewelry_manufacturer; [business_to_business,business_manufacturing_and_supply,jewelry_manufacturer] +leather_products_manufacturer; [business_to_business,business_manufacturing_and_supply,leather_products_manufacturer] +lighting_fixture_manufacturers; [business_to_business,business_manufacturing_and_supply,lighting_fixture_manufacturers] +pipe_supplier; [business_to_business,business_manufacturing_and_supply,pipe_supplier] +plastic_fabrication_company; [business_to_business,business_manufacturing_and_supply,plastic_fabrication_company] +plastic_injection_molding_workshop; [business_to_business,business_manufacturing_and_supply,plastic_injection_molding_workshop] +printing_equipment_and_supply; [business_to_business,business_manufacturing_and_supply,printing_equipment_and_supply] +retaining_wall_supplier; [business_to_business,business_manufacturing_and_supply,retaining_wall_supplier] +sand_and_gravel_supplier; [business_to_business,business_manufacturing_and_supply,sand_and_gravel_supplier] +scale_supplier; [business_to_business,business_manufacturing_and_supply,scale_supplier] +seal_and_hanko_dealers; [business_to_business,business_manufacturing_and_supply,seal_and_hanko_dealers] +shoe_factory; [business_to_business,business_manufacturing_and_supply,shoe_factory] +spring_supplier; [business_to_business,business_manufacturing_and_supply,spring_supplier] +stone_supplier; [business_to_business,business_manufacturing_and_supply,stone_supplier] +turnery; [business_to_business,business_manufacturing_and_supply,turnery] +window_supplier; [business_to_business,business_manufacturing_and_supply,window_supplier] +metals; [business_to_business,business_manufacturing_and_supply,metals] +metal_supplier; [business_to_business,business_manufacturing_and_supply,metals,metal_supplier] +metal_plating_service; [business_to_business,business_manufacturing_and_supply,metals,metal_plating_service] +metal_fabricator; [business_to_business,business_manufacturing_and_supply,metals,metal_fabricator] +iron_and_steel_industry; [business_to_business,business_manufacturing_and_supply,metals,metal_fabricator,iron_and_steel_industry] +iron_work; [business_to_business,business_manufacturing_and_supply,metals,metal_fabricator,iron_and_steel_industry,iron_work] +scrap_metals; [business_to_business,business_manufacturing_and_supply,metals,scrap_metals] +sheet_metal; [business_to_business,business_manufacturing_and_supply,metals,sheet_metal] +steel_fabricators; [business_to_business,business_manufacturing_and_supply,metals,steel_fabricators] +mills; [business_to_business,business_manufacturing_and_supply,mills] +cotton_mill; [business_to_business,business_manufacturing_and_supply,mills,cotton_mill] +flour_mill; [business_to_business,business_manufacturing_and_supply,mills,flour_mill] +paper_mill; [business_to_business,business_manufacturing_and_supply,mills,paper_mill] +rice_mill; [business_to_business,business_manufacturing_and_supply,mills,rice_mill] +saw_mill; [business_to_business,business_manufacturing_and_supply,mills,saw_mill] +textile_mill; [business_to_business,business_manufacturing_and_supply,mills,textile_mill] +weaving_mill; [business_to_business,business_manufacturing_and_supply,mills,weaving_mill] +wood_and_pulp; [business_to_business,business_manufacturing_and_supply,wood_and_pulp] +logging_equipment_and_supplies; [business_to_business,business_manufacturing_and_supply,wood_and_pulp,logging_equipment_and_supplies] +logging_services; [business_to_business,business_manufacturing_and_supply,wood_and_pulp,logging_services] +logging_contractor; [business_to_business,business_manufacturing_and_supply,wood_and_pulp,logging_contractor] +b2b_medical_support_services; [business_to_business,b2b_medical_support_services] +biotechnology_company; [business_to_business,b2b_medical_support_services,biotechnology_company] +clinical_laboratories; [business_to_business,b2b_medical_support_services,clinical_laboratories] +dental_laboratories; [business_to_business,b2b_medical_support_services,dental_laboratories] +hospital_equipment_and_supplies; [business_to_business,b2b_medical_support_services,hospital_equipment_and_supplies] +medical_research_and_development; [business_to_business,b2b_medical_support_services,medical_research_and_development] +pharmaceutical_companies; [business_to_business,b2b_medical_support_services,pharmaceutical_companies] +surgical_appliances_and_supplies; [business_to_business,b2b_medical_support_services,surgical_appliances_and_supplies] +b2b_science_and_technology; [business_to_business,b2b_science_and_technology] +b2b_scientific_equipment; [business_to_business,b2b_science_and_technology,b2b_scientific_equipment] +research_institute; [business_to_business,b2b_science_and_technology,research_institute] +scientific_laboratories; [business_to_business,b2b_science_and_technology,scientific_laboratories] +business_advertising; [business_to_business,business_advertising] +business_signage; [business_to_business,business_advertising,business_signage] +direct_mail_advertising; [business_to_business,business_advertising,direct_mail_advertising] +marketing_consultant; [business_to_business,business_advertising,marketing_consultant] +newspaper_advertising; [business_to_business,business_advertising,newspaper_advertising] +outdoor_advertising; [business_to_business,business_advertising,outdoor_advertising] +promotional_products_and_services; [business_to_business,business_advertising,promotional_products_and_services] +publicity_service; [business_to_business,business_advertising,publicity_service] +radio_and_television_commercials; [business_to_business,business_advertising,radio_and_television_commercials] +telemarketing_services; [business_to_business,business_advertising,telemarketing_services] +business_equipment_and_supply; [business_to_business,business_equipment_and_supply] +beauty_product_supplier; [business_to_business,business_equipment_and_supply,beauty_product_supplier] +beverage_supplier; [business_to_business,business_equipment_and_supply,beverage_supplier] +business_office_supplies_and_stationery; [business_to_business,business_equipment_and_supply,business_office_supplies_and_stationery] +electronic_parts_supplier; [business_to_business,business_equipment_and_supply,electronic_parts_supplier] +energy_equipment_and_solution; [business_to_business,business_equipment_and_supply,energy_equipment_and_solution] +hydraulic_equipment_supplier; [business_to_business,business_equipment_and_supply,hydraulic_equipment_supplier] +laboratory_equipment_supplier; [business_to_business,business_equipment_and_supply,laboratory_equipment_supplier] +thread_supplier; [business_to_business,business_equipment_and_supply,thread_supplier] +vending_machine_supplier; [business_to_business,business_equipment_and_supply,vending_machine_supplier] +water_softening_equipment_supplier; [business_to_business,business_equipment_and_supply,water_softening_equipment_supplier] +wholesaler; [business_to_business,business_equipment_and_supply,wholesaler] +wholesale_grocer; [business_to_business,business_equipment_and_supply,wholesaler,wholesale_grocer] +spices_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,fmcg_wholesaler,spices_wholesaler] +computer_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,computer_wholesaler] +electrical_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,electrical_wholesaler] +fabric_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,fabric_wholesaler] +fitness_equipment_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,fitness_equipment_wholesaler] +fmcg_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,fmcg_wholesaler] +footwear_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,footwear_wholesaler] +greengrocer; [business_to_business,business_equipment_and_supply,wholesaler,greengrocer] +industrial_spares_and_products_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,industrial_spares_and_products_wholesaler] +iron_and_steel_store; [business_to_business,business_equipment_and_supply,wholesaler,iron_and_steel_store] +lingerie_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,lingerie_wholesaler] +meat_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,meat_wholesaler] +optical_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,optical_wholesaler] +pharmaceutical_products_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,pharmaceutical_products_wholesaler] +produce_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,produce_wholesaler] +seafood_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,seafood_wholesaler] +tea_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,tea_wholesaler] +threads_and_yarns_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,threads_and_yarns_wholesaler] +tools_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,tools_wholesaler] +wholesale_florist; [business_to_business,business_equipment_and_supply,wholesaler,wholesale_florist] +wine_wholesaler; [business_to_business,business_equipment_and_supply,wholesaler,wine_wholesaler] +restaurant_wholesale; [business_to_business,business_equipment_and_supply,wholesaler,restaurant_wholesale] +restaurant_equipment_and_supply; [business_to_business,business_equipment_and_supply,restaurant_equipment_and_supply] +business_storage_and_transportation; [business_to_business,business_storage_and_transportation] +motor_freight_trucking; [business_to_business,business_storage_and_transportation,motor_freight_trucking] +pipeline_transportation; [business_to_business,business_storage_and_transportation,pipeline_transportation] +railroad_freight; [business_to_business,business_storage_and_transportation,railroad_freight] +b2b_storage_and_warehouses; [business_to_business,business_storage_and_transportation,b2b_storage_and_warehouses] +warehouse_rental_services_and_yards; [business_to_business,business_storage_and_transportation,b2b_storage_and_warehouses,warehouse_rental_services_and_yards] +warehouses; [business_to_business,business_storage_and_transportation,b2b_storage_and_warehouses,warehouses] +freight_and_cargo_service; [business_to_business,business_storage_and_transportation,freight_and_cargo_service] +distribution_services; [business_to_business,business_storage_and_transportation,freight_and_cargo_service,distribution_services] +freight_forwarding_agency; [business_to_business,business_storage_and_transportation,freight_and_cargo_service,freight_forwarding_agency] +trucks_and_industrial_vehicles; [business_to_business,business_storage_and_transportation,trucks_and_industrial_vehicles] +b2b_forklift_dealers; [business_to_business,business_storage_and_transportation,trucks_and_industrial_vehicles,b2b_forklift_dealers] +b2b_tractor_dealers; [business_to_business,business_storage_and_transportation,trucks_and_industrial_vehicles,b2b_tractor_dealers] +b2b_truck_equipment_parts_and_accessories; [business_to_business,business_storage_and_transportation,trucks_and_industrial_vehicles,b2b_truck_equipment_parts_and_accessories] +truck_dealer_for_businesses; [business_to_business,business_storage_and_transportation,trucks_and_industrial_vehicles,truck_dealer_for_businesses] +truck_repair_and_services_for_businesses; [business_to_business,business_storage_and_transportation,trucks_and_industrial_vehicles,truck_repair_and_services_for_businesses] +business_to_business_services; [business_to_business,business_to_business_services] +agricultural_production; [business_to_business,business_to_business_services,agricultural_production] +audio_visual_production_and_design; [business_to_business,business_to_business_services,audio_visual_production_and_design] +boat_builder; [business_to_business,business_to_business_services,boat_builder] +business_records_storage_and_management; [business_to_business,business_to_business_services,business_records_storage_and_management] +coworking_space; [business_to_business,business_to_business_services,coworking_space] +domestic_business_and_trade_organizations; [business_to_business,business_to_business_services,domestic_business_and_trade_organizations] +human_resource_services; [business_to_business,business_to_business_services,human_resource_services] +information_technology_company; [business_to_business,business_to_business_services,information_technology_company] +laser_cutting_service_provider; [business_to_business,business_to_business_services,laser_cutting_service_provider] +telecommunications_company; [business_to_business,business_to_business_services,telecommunications_company] +tower_communication_service; [business_to_business,business_to_business_services,tower_communication_service] +transcription_services; [business_to_business,business_to_business_services,transcription_services] +translating_and_interpreting_services; [business_to_business,business_to_business_services,translating_and_interpreting_services] +consultant_and_general_service; [business_to_business,business_to_business_services,consultant_and_general_service] +food_consultant; [business_to_business,business_to_business_services,consultant_and_general_service,food_consultant] +business_management_services; [business_to_business,business_to_business_services,consultant_and_general_service,business_management_services] +executive_search_consultants; [business_to_business,business_to_business_services,consultant_and_general_service,executive_search_consultants] +manufacturing_and_industrial_consultant; [business_to_business,business_to_business_services,consultant_and_general_service,manufacturing_and_industrial_consultant] +secretarial_services; [business_to_business,business_to_business_services,consultant_and_general_service,secretarial_services] +manufacturers_agents_and_representatives; [business_to_business,business_to_business_services,domestic_business_and_trade_organizations,manufacturers_agents_and_representatives] +environmental_and_ecological_services_for_businesses; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses] +b2b_cleaning_and_waste_management; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses,b2b_cleaning_and_waste_management] +water_treatment_equipment_and_services; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses,b2b_cleaning_and_waste_management,water_treatment_equipment_and_services] +energy_management_and_conservation_consultants; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses,energy_management_and_conservation_consultants] +environmental_conservation_and_ecological_organizations; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses,environmental_conservation_and_ecological_organizations] +environmental_renewable_natural_resource; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses,environmental_renewable_natural_resource] +forestry_consultants; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses,forestry_consultants] +geological_services; [business_to_business,business_to_business_services,environmental_and_ecological_services_for_businesses,geological_services] +food_and_beverage_exporter; [business_to_business,business_to_business_services,international_business_and_trade_services,importer_and_exporter,exporters,food_and_beverage_exporter] +background_check_services; [business_to_business,business_to_business_services,human_resource_services,background_check_services] +international_business_and_trade_services; [business_to_business,business_to_business_services,international_business_and_trade_services] +importer_and_exporter; [business_to_business,business_to_business_services,international_business_and_trade_services,importer_and_exporter] +exporters; [business_to_business,business_to_business_services,international_business_and_trade_services,importer_and_exporter,exporters] +importers; [business_to_business,business_to_business_services,international_business_and_trade_services,importer_and_exporter,importers] +restaurant_management; [business_to_business,business_to_business_services,restaurant_management] +b2b_energy_mining; [business_to_business,b2b_energy_and_mining] +mining; [business_to_business,b2b_energy_and_mining,mining] +coal_and_coke; [business_to_business,b2b_energy_and_mining,mining,coal_and_coke] +quarries; [business_to_business,b2b_energy_and_mining,mining,quarries] +oil_and_gas; [business_to_business,b2b_energy_and_mining,oil_and_gas] +b2b_oil_and_gas_extraction_and_services; [business_to_business,b2b_energy_and_mining,oil_and_gas,b2b_oil_and_gas_extraction_and_services] +oil_and_gas_exploration_and_development; [business_to_business,b2b_energy_and_mining,oil_and_gas,oil_and_gas_exploration_and_development] +oil_and_gas_field_equipment_and_services; [business_to_business,b2b_energy_and_mining,oil_and_gas,oil_and_gas_field_equipment_and_services] +oil_refiners; [business_to_business,b2b_energy_and_mining,oil_and_gas,oil_refiners] +power_plants_and_power_plant_service; [business_to_business,b2b_energy_and_mining,power_plants_and_power_plant_service] +public_service_and_government; [public_service_and_government] +energy_company; [public_service_and_government,public_utility_company,energy_company] +electric_utility_provider; [public_service_and_government,public_utility_company,electric_utility_provider] +public_utility_company; [public_service_and_government,public_utility_company] +law_enforcement; [public_service_and_government,law_enforcement] +organization; [public_service_and_government,organization] +agriculture_association; [public_service_and_government,organization,agriculture_association] +environmental_conservation_organization; [public_service_and_government,organization,environmental_conservation_organization] +home_organization; [public_service_and_government,organization,home_organization] +labor_union; [public_service_and_government,organization,labor_union] +non_governmental_association; [public_service_and_government,organization,non_governmental_association] +political_organization; [public_service_and_government,organization,political_organization] +private_association; [public_service_and_government,organization,private_association] +public_and_government_association; [public_service_and_government,organization,public_and_government_association] +social_service_organizations; [public_service_and_government,organization,social_service_organizations] +charity_organization; [public_service_and_government,organization,social_service_organizations,charity_organization] +food_banks; [public_service_and_government,organization,social_service_organizations,food_banks] +foster_care_services; [public_service_and_government,organization,social_service_organizations,foster_care_services] +gay_and_lesbian_services_organization; [public_service_and_government,organization,social_service_organizations,gay_and_lesbian_services_organization] +homeless_shelter; [public_service_and_government,organization,social_service_organizations,homeless_shelter] +housing_authorities; [public_service_and_government,organization,social_service_organizations,housing_authorities] +senior_citizen_services; [public_service_and_government,organization,social_service_organizations,senior_citizen_services] +social_and_human_services; [public_service_and_government,organization,social_service_organizations,social_and_human_services] +social_welfare_center; [public_service_and_government,organization,social_service_organizations,social_welfare_center] +volunteer_association; [public_service_and_government,organization,social_service_organizations,volunteer_association] +child_protection_service; [public_service_and_government,organization,social_service_organizations,child_protection_service] +youth_organizations; [public_service_and_government,organization,social_service_organizations,youth_organizations] +community_services_non_profits; [public_service_and_government,community_services] +disability_services_and_support_organization; [public_service_and_government,community_services,disability_services_and_support_organization] +government_services; [public_service_and_government,government_services] +social_security_services; [public_service_and_government,government_services,social_security_services] +jail_and_prison; [public_service_and_government,jail_and_prison] +juvenile_detention_center; [public_service_and_government,jail_and_prison,juvenile_detention_center] +post_office; [public_service_and_government,post_office] +shipping_collection_services; [public_service_and_government,post_office,shipping_collection_services] +public_toilet; [public_service_and_government,public_toilet] +armed_forces_branch; [public_service_and_government,armed_forces_branch] +central_government_office; [public_service_and_government,central_government_office] +chambers_of_commerce; [public_service_and_government,chambers_of_commerce] +children_hall; [public_service_and_government,children_hall] +civic_center; [public_service_and_government,civic_center] +community_center; [public_service_and_government,community_center] +courthouse; [public_service_and_government,courthouse] +department_of_motor_vehicles; [public_service_and_government,department_of_motor_vehicles] +department_of_social_service; [public_service_and_government,department_of_social_service] +embassy; [public_service_and_government,embassy] +family_service_center; [public_service_and_government,family_service_center] +federal_government_offices; [public_service_and_government,federal_government_offices] +fire_department; [public_service_and_government,fire_department] +immigration_and_naturalization; [public_service_and_government,immigration_and_naturalization] +library; [public_service_and_government,library] +local_and_state_government_offices; [public_service_and_government,local_and_state_government_offices] +low_income_housing; [public_service_and_government,low_income_housing] +national_security_services; [public_service_and_government,national_security_services] +office_of_vital_records; [public_service_and_government,office_of_vital_records] +pension; [public_service_and_government,pension] +police_department; [public_service_and_government,police_department] +political_party_office; [public_service_and_government,political_party_office] +railway_service; [public_service_and_government,railway_service] +registry_office; [public_service_and_government,registry_office] +retirement_home; [public_service_and_government,retirement_home] +scout_hall; [public_service_and_government,scout_hall] +tax_office; [public_service_and_government,tax_office] +town_hall; [public_service_and_government,town_hall] +unemployment_office; [public_service_and_government,unemployment_office] +weather_station; [public_service_and_government,weather_station] +religious_organization; [religious_organization] +church_cathedral; [religious_organization,church_cathedral] +anglican_church; [religious_organization,church_cathedral,anglican_church] +baptist_church; [religious_organization,church_cathedral,baptist_church] +catholic_church; [religious_organization,church_cathedral,catholic_church] +episcopal_church; [religious_organization,church_cathedral,episcopal_church] +evangelical_church; [religious_organization,church_cathedral,evangelical_church] +jehovahs_witness_kingdom_hall; [religious_organization,church_cathedral,jehovahs_witness_kingdom_hall] +pentecostal_church; [religious_organization,church_cathedral,pentecostal_church] +mission; [religious_organization,mission] +buddhist_temple; [religious_organization,buddhist_temple] +convents_and_monasteries; [religious_organization,convents_and_monasteries] +hindu_temple; [religious_organization,hindu_temple] +mosque; [religious_organization,mosque] +religious_destination; [religious_organization,religious_destination] +shinto_shrines; [religious_organization,shinto_shrines] +sikh_temple; [religious_organization,sikh_temple] +synagogue; [religious_organization,synagogue] +temple; [religious_organization,temple] +real_estate; [real_estate] +real_estate_investment; [real_estate,real_estate_investment] +builders; [real_estate,builders] +home_developer; [real_estate,builders,home_developer] +apartments; [real_estate,apartments] +art_space_rental; [real_estate,art_space_rental] +commercial_real_estate; [real_estate,commercial_real_estate] +condominium; [real_estate,condominium] +display_home_center; [real_estate,display_home_center] +estate_liquidation; [real_estate,estate_liquidation] +holiday_park; [real_estate,holiday_park] +home_staging; [real_estate,home_staging] +homeowner_association; [real_estate,homeowner_association] +housing_cooperative; [real_estate,housing_cooperative] +kitchen_incubator; [real_estate,kitchen_incubator] +mobile_home_dealer; [real_estate,mobile_home_dealer] +mobile_home_park; [real_estate,mobile_home_park] +mortgage_broker; [real_estate,mortgage_broker] +property_management; [real_estate,property_management] +shared_office_space; [real_estate,shared_office_space] +university_housing; [real_estate,university_housing] +real_estate_agent; [real_estate,real_estate_agent] +apartment_agent; [real_estate,real_estate_agent,apartment_agent] +real_estate_service; [real_estate,real_estate_service] +escrow_services; [real_estate,real_estate_service,escrow_services] +land_surveying; [real_estate,real_estate_service,land_surveying] +real_estate_photography; [real_estate,real_estate_service,real_estate_photography] +rental_services; [real_estate,real_estate_service,rental_services] +travel; [travel] +airport; [travel,airport] +airport_terminal; [travel,airport,airport_terminal] +balloon_ports; [travel,airport,balloon_ports] +domestic_airports; [travel,airport,domestic_airports] +gliderports; [travel,airport,gliderports] +heliports; [travel,airport,heliports] +major_airports; [travel,airport,major_airports] +seaplane_bases; [travel,airport,seaplane_bases] +ultralight_airports; [travel,airport,ultralight_airports] +travel_services; [travel,travel_services] +passport_and_visa_services; [travel,travel_services,passport_and_visa_services] +visa_agent; [travel,travel_services,passport_and_visa_services,visa_agent] +travel_agents; [travel,travel_services,travel_agents] +sightseeing_tour_agency; [travel,travel_services,travel_agents,sightseeing_tour_agency] +luggage_storage; [travel,travel_services,luggage_storage] +visitor_center; [travel,travel_services,visitor_center] +road_structures_and_services; [travel,road_structures_and_services] +rest_areas; [travel,road_structures_and_services,rest_areas] +toll_stations; [travel,road_structures_and_services,toll_stations] +tours; [travel,tours] +aerial_tours; [travel,tours,aerial_tours] +architectural_tours; [travel,tours,architectural_tours] +art_tours; [travel,tours,art_tours] +beer_tours; [travel,tours,beer_tours] +bike_tours; [travel,tours,bike_tours] +boat_tours; [travel,tours,boat_tours] +bus_tours; [travel,tours,bus_tours] +cannabis_tour; [travel,tours,cannabis_tour] +food_tours; [travel,tours,food_tours] +historical_tours; [travel,tours,historical_tours] +scooter_tours; [travel,tours,scooter_tours] +walking_tours; [travel,tours,walking_tours] +whale_watching_tours; [travel,tours,whale_watching_tours] +wine_tours; [travel,tours,wine_tours] +transportation; [travel,transportation] +trains; [travel,transportation,trains] +train_station; [travel,transportation,trains,train_station] +airlines; [travel,transportation,airlines] +airport_shuttles; [travel,transportation,airport_shuttles] +bicycle_sharing_location; [travel,transportation,bicycle_sharing_location] +bike_parking; [travel,transportation,bike_parking] +bike_sharing; [travel,transportation,bike_sharing] +bus_service; [travel,transportation,bus_service] +bus_station; [travel,transportation,bus_station] +cable_car_service; [travel,transportation,cable_car_service] +car_sharing; [travel,transportation,car_sharing] +coach_bus; [travel,transportation,coach_bus] +ferry_service; [travel,transportation,ferry_service] +light_rail_and_subway_stations; [travel,transportation,light_rail_and_subway_stations] +limo_services; [travel,transportation,limo_services] +metro_station; [travel,transportation,metro_station] +motorcycle_parking; [travel,transportation,motorcycle_parking] +park_and_rides; [travel,transportation,park_and_rides] +parking; [travel,transportation,parking] +pedicab_service; [travel,transportation,pedicab_service] +private_jet_charters; [travel,transportation,private_jet_charters] +public_transportation; [travel,transportation,public_transportation] +ride_sharing; [travel,transportation,ride_sharing] +taxi_rank; [travel,transportation,taxi_rank] +taxi_service; [travel,transportation,taxi_service] +town_car_service; [travel,transportation,town_car_service] +transport_interchange; [travel,transportation,transport_interchange] +water_taxi; [travel,transportation,water_taxi] +agriturismo; [travel,agriturismo] +airline_ticket_agency; [travel,airline_ticket_agency] +bus_ticket_agency; [travel,bus_ticket_agency] +rental_service; [travel,rental_service] +car_rental_agency; [travel,rental_service,car_rental_agency] +motorcycle_rentals; [travel,rental_service,motorcycle_rentals] +rv_rentals; [travel,rental_service,rv_rentals] +trailer_rentals; [travel,rental_service,trailer_rentals] +truck_rentals; [travel,rental_service,truck_rentals] +country_house; [travel,country_house] +houseboat; [travel,houseboat] +railway_ticket_agent; [travel,railway_ticket_agent] +rest_stop; [travel,rest_stop] +ryokan; [travel,ryokan] +self_catering_accommodation; [travel,self_catering_accommodation] +ski_resort; [travel,ski_resort] +vacation_rental_agents; [travel,vacation_rental_agents] +mass_media; [mass_media] +print_media; [mass_media,print_media] +media_critic; [mass_media,media_critic] +movie_critic; [mass_media,media_critic,movie_critic] +music_critic; [mass_media,media_critic,music_critic] +video_game_critic; [mass_media,media_critic,video_game_critic] +media_news_company; [mass_media,media_news_company] +media_agency; [mass_media,media_news_company,media_agency] +radio_station; [mass_media,media_news_company,radio_station] +television_station; [mass_media,media_news_company,television_station] +weather_forecast_services; [mass_media,media_news_company,weather_forecast_services] +animation_studio; [mass_media,media_news_company,animation_studio] +book_magazine_distribution; [mass_media,media_news_company,book_magazine_distribution] +broadcasting_media_production; [mass_media,media_news_company,broadcasting_media_production] +game_publisher; [mass_media,media_news_company,game_publisher] +movie_television_studio; [mass_media,media_news_company,movie_television_studio] +music_production; [mass_media,media_news_company,music_production] +topic_publisher; [mass_media,media_news_company,topic_publisher] +social_media_company; [mass_media,media_news_company,social_media_company] +media_news_website; [mass_media,media_news_website] +media_restoration_service; [mass_media,media_restoration_service] +art_restoration; [mass_media,media_restoration_service,art_restoration] +theatrical_productions; [mass_media,treatrical_productions] +home_service; [home_service] +ceiling_and_roofing_repair_and_service; [home_service,ceiling_and_roofing_repair_and_service] +ceiling_service; [home_service,ceiling_and_roofing_repair_and_service,ceiling_service] +roofing; [home_service,ceiling_and_roofing_repair_and_service,roofing] +chimney_service; [home_service,chimney_service] +chimney_sweep; [home_service,chimney_service,chimney_sweep] +contractor; [home_service,contractor] +altering_and_remodeling_contractor; [home_service,contractor,altering_and_remodeling_contractor] +building_contractor; [home_service,contractor,building_contractor] +flooring_contractors; [home_service,contractor,flooring_contractors] +paving_contractor; [home_service,contractor,paving_contractor] +damage_restoration; [home_service,damage_restoration] +fire_and_water_damage_restoration; [home_service,damage_restoration,fire_and_water_damage_restoration] +artificial_turf; [home_service,artificial_turf] +bathroom_remodeling; [home_service,bathroom_remodeling] +bathtub_and_sink_repairs; [home_service,bathtub_and_sink_repairs] +cabinet_sales_service; [home_service,cabinet_sales_service] +carpenter; [home_service,carpenter] +carpet_cleaning; [home_service,carpet_cleaning] +carpet_installation; [home_service,carpet_installation] +childproofing; [home_service,childproofing] +closet_remodeling; [home_service,closet_remodeling] +countertop_installation; [home_service,countertop_installation] +deck_and_railing_sales_service; [home_service,deck_and_railing_sales_service] +demolition_service; [home_service,demolition_service] +door_sales_service; [home_service,door_sales_service] +drywall_services; [home_service,drywall_services] +electrician; [home_service,electrician] +excavation_service; [home_service,excavation_service] +exterior_design; [home_service,exterior_design] +fence_and_gate_sales_service; [home_service,fence_and_gate_sales_service] +fire_protection_service; [home_service,fire_protection_service] +fireplace_service; [home_service,fireplace_service] +firewood; [home_service,firewood] +foundation_repair; [home_service,foundation_repair] +furniture_assembly; [home_service,furniture_assembly] +garage_door_service; [home_service,garage_door_service] +glass_and_mirror_sales_service; [home_service,glass_and_mirror_sales_service] +grout_service; [home_service,grout_service] +gutter_service; [home_service,gutter_service] +handyman; [home_service,handyman] +holiday_decorating; [home_service,holiday_decorating] +home_automation; [home_service,home_automation] +home_cleaning; [home_service,home_cleaning] +home_energy_auditor; [home_service,home_energy_auditor] +home_inspector; [home_service,home_inspector] +home_network_installation; [home_service,home_network_installation] +home_security; [home_service,home_security] +home_window_tinting; [home_service,home_window_tinting] +house_sitting; [home_service,house_sitting] +hvac_services; [home_service,hvac_services] +insulation_installation; [home_service,insulation_installation] +interior_design; [home_service,interior_design] +irrigation; [home_service,irrigation] +key_and_locksmith; [home_service,key_and_locksmith] +kitchen_remodeling; [home_service,kitchen_remodeling] +lighting_fixtures_and_equipment; [home_service,lighting_fixtures_and_equipment] +masonry_concrete; [home_service,masonry_concrete] +mobile_home_repair; [home_service,mobile_home_repair] +movers; [home_service,movers] +packing_services; [home_service,packing_services] +painting; [home_service,painting] +patio_covers; [home_service,patio_covers] +plasterer; [home_service,plasterer] +pool_and_hot_tub_services; [home_service,pool_and_hot_tub_services] +pool_cleaning; [home_service,pool_cleaning] +pressure_washing; [home_service,pressure_washing] +refinishing_services; [home_service,refinishing_services] +security_systems; [home_service,security_systems] +shades_and_blinds; [home_service,shades_and_blinds] +shutters; [home_service,shutters] +siding; [home_service,siding] +solar_installation; [home_service,solar_installation] +solar_panel_cleaning; [home_service,solar_panel_cleaning] +structural_engineer; [home_service,structural_engineer] +stucco_services; [home_service,stucco_services] +television_service_providers; [home_service,television_service_providers] +tiling; [home_service,tiling] +wallpaper_installers; [home_service,wallpaper_installers] +washer_and_dryer_repair_service; [home_service,washer_and_dryer_repair_service] +water_heater_installation_repair; [home_service,water_heater_installation_repair] +water_purification_services; [home_service,water_purification_services] +waterproofing; [home_service,waterproofing] +window_washing; [home_service,window_washing] +landscaping; [home_service,landscaping] +gardener; [home_service,landscaping,gardener] +landscape_architect; [home_service,landscaping,landscape_architect] +lawn_service; [home_service,landscaping,lawn_service] +tree_services; [home_service,landscaping,tree_services] +plumbing; [home_service,plumbing] +backflow_services; [home_service,plumbing,backflow_services] +windows_installation; [home_service,windows_installation] +skylight_installation; [home_service,windows_installation,skylight_installation] +professional_services; [professional_services] +cleaning_services; [professional_services,cleaning_services] +industrial_cleaning_services; [professional_services,cleaning_services,industrial_cleaning_services] +janitorial_services; [professional_services,cleaning_services,janitorial_services] +office_cleaning; [professional_services,cleaning_services,office_cleaning] +construction_services; [professional_services,construction_services] +blueprinters; [professional_services,construction_services,blueprinters] +construction_management; [professional_services,construction_services,construction_management] +inspection_services; [professional_services,construction_services,inspection_services] +road_contractor; [professional_services,construction_services,road_contractor] +wind_energy; [professional_services,construction_services,wind_energy] +engineering_services; [professional_services,construction_services,engineering_services] +civil_engineers; [professional_services,construction_services,engineering_services,civil_engineers] +instrumentation_engineers; [professional_services,construction_services,engineering_services,instrumentation_engineers] +mechanical_engineers; [professional_services,construction_services,engineering_services,mechanical_engineers] +metal_materials_and_experts; [professional_services,construction_services,metal_materials_and_experts] +blacksmiths; [professional_services,construction_services,metal_materials_and_experts,blacksmiths] +ironworkers; [professional_services,construction_services,metal_materials_and_experts,ironworkers] +welders; [professional_services,construction_services,metal_materials_and_experts,welders] +stone_and_masonry; [professional_services,construction_services,stone_and_masonry] +gravel_professionals; [professional_services,construction_services,stone_and_masonry,gravel_professionals] +lime_professionals; [professional_services,construction_services,stone_and_masonry,lime_professionals] +marble_and_granite_professionals; [professional_services,construction_services,stone_and_masonry,marble_and_granite_professionals] +masonry_contractors; [professional_services,construction_services,stone_and_masonry,masonry_contractors] +lawyer; [professional_services,lawyer] +estate_planning_law; [professional_services,lawyer,estate_planning_law] +wills_trusts_and_probate; [professional_services,lawyer,estate_planning_law,wills_trusts_and_probate] +appellate_practice_lawyers; [professional_services,lawyer,appellate_practice_lawyers] +bankruptcy_law; [professional_services,lawyer,bankruptcy_law] +business_law; [professional_services,lawyer,business_law] +civil_rights_lawyers; [professional_services,lawyer,civil_rights_lawyers] +contract_law; [professional_services,lawyer,contract_law] +criminal_defense_law; [professional_services,lawyer,criminal_defense_law] +disability_law; [professional_services,lawyer,disability_law] +divorce_and_family_law; [professional_services,lawyer,divorce_and_family_law] +dui_law; [professional_services,lawyer,dui_law] +employment_law; [professional_services,lawyer,employment_law] +entertainment_law; [professional_services,lawyer,entertainment_law] +general_litigation; [professional_services,lawyer,general_litigation] +immigration_law; [professional_services,lawyer,immigration_law] +ip_and_internet_law; [professional_services,lawyer,ip_and_internet_law] +medical_law; [professional_services,lawyer,medical_law] +paralegal_services; [professional_services,lawyer,paralegal_services] +personal_injury_law; [professional_services,lawyer,personal_injury_law] +real_estate_law; [professional_services,lawyer,real_estate_law] +social_security_law; [professional_services,lawyer,social_security_law] +tax_law; [professional_services,lawyer,tax_law] +traffic_ticketing_law; [professional_services,lawyer,traffic_ticketing_law] +workers_compensation_law; [professional_services,lawyer,workers_compensation_law] +event_planning; [professional_services,event_planning] +balloon_services; [professional_services,event_planning,balloon_services] +bartender; [professional_services,event_planning,bartender] +boat_charter; [professional_services,event_planning,boat_charter] +caricature; [professional_services,event_planning,caricature] +caterer; [professional_services,event_planning,caterer] +clown; [professional_services,event_planning,clown] +dj_service; [professional_services,event_planning,dj_service] +event_technology_service; [professional_services,event_planning,event_technology_service] +face_painting; [professional_services,event_planning,face_painting] +floral_designer; [professional_services,event_planning,floral_designer] +game_truck_rental; [professional_services,event_planning,game_truck_rental] +golf_cart_rental; [professional_services,event_planning,golf_cart_rental] +henna_artist; [professional_services,event_planning,henna_artist] +kids_recreation_and_party; [professional_services,event_planning,kids_recreation_and_party] +magician; [professional_services,event_planning,magician] +mohel; [professional_services,event_planning,mohel] +musician; [professional_services,event_planning,musician] +officiating_services; [professional_services,event_planning,officiating_services] +party_and_event_planning; [professional_services,event_planning,party_and_event_planning] +party_bike_rental; [professional_services,event_planning,party_bike_rental] +party_bus_rental; [professional_services,event_planning,party_bus_rental] +party_character; [professional_services,event_planning,party_character] +personal_chef; [professional_services,event_planning,personal_chef] +photo_booth_rental; [professional_services,event_planning,photo_booth_rental] +silent_disco; [professional_services,event_planning,silent_disco] +sommelier_service; [professional_services,event_planning,sommelier_service] +team_building_activity; [professional_services,event_planning,team_building_activity] +trivia_host; [professional_services,event_planning,trivia_host] +valet_service; [professional_services,event_planning,valet_service] +venue_and_event_space; [professional_services,event_planning,venue_and_event_space] +videographer; [professional_services,event_planning,videographer] +wedding_chapel; [professional_services,event_planning,wedding_chapel] +wedding_planning; [professional_services,event_planning,wedding_planning] +party_equipment_rental; [professional_services,event_planning,party_equipment_rental] +audiovisual_equipment_rental; [professional_services,event_planning,party_equipment_rental,audiovisual_equipment_rental] +bounce_house_rental; [professional_services,event_planning,party_equipment_rental,bounce_house_rental] +karaoke_rental; [professional_services,event_planning,party_equipment_rental,karaoke_rental] +photographer; [professional_services,event_planning,photographer] +boudoir_photography; [professional_services,event_planning,photographer,boudoir_photography] +event_photography; [professional_services,event_planning,photographer,event_photography] +session_photography; [professional_services,event_planning,photographer,session_photography] +funeral_services_and_cemeteries; [professional_services,funeral_services_and_cemeteries] +cremation_services; [professional_services,funeral_services_and_cemeteries,cremation_services] +mortuary_services; [professional_services,funeral_services_and_cemeteries,mortuary_services] +internet_service_provider; [professional_services,internet_service_provider] +web_hosting_service; [professional_services,internet_service_provider,web_hosting_service] +it_service_and_computer_repair; [professional_services,it_service_and_computer_repair] +data_recovery; [professional_services,it_service_and_computer_repair,data_recovery] +it_consultant; [professional_services,it_service_and_computer_repair,it_consultant] +it_support_snd_service; [professional_services,it_service_and_computer_repair,it_support_snd_service] +mobile_phone_repair; [professional_services,it_service_and_computer_repair,mobile_phone_repair] +telecommunications; [professional_services,it_service_and_computer_repair,telecommunications] +junk_removal_and_hauling; [professional_services,junk_removal_and_hauling] +dumpster_rentals; [professional_services,junk_removal_and_hauling,dumpster_rentals] +laundry_services; [professional_services,laundry_services] +dry_cleaning; [professional_services,laundry_services,dry_cleaning] +laundromat; [professional_services,laundry_services,laundromat] +legal_services; [professional_services,legal_services] +court_reporter; [professional_services,legal_services,court_reporter] +process_servers; [professional_services,legal_services,process_servers] +musical_instrument_services; [professional_services,musical_instrument_services] +piano_services; [professional_services,musical_instrument_services,piano_services] +vocal_coach; [professional_services,musical_instrument_services,vocal_coach] +3d_printing_service; [professional_services,3d_printing_service] +acoustical_consultant; [professional_services,acoustical_consultant] +adoption_services; [professional_services,adoption_services] +advertising_agency; [professional_services,advertising_agency] +copywriting_service; [professional_services,copywriting_service] +internet_marketing_service; [professional_services,internet_marketing_service] +merchandising_service; [professional_services,merchandising_service] +social_media_agency; [professional_services,social_media_agency] +after_school_program; [professional_services,after_school_program] +bank_equipment_service; [professional_services,bank_equipment_service] +air_duct_cleaning_service; [professional_services,air_duct_cleaning_service] +antenna_service; [professional_services,antenna_service] +appliance_repair_service; [professional_services,appliance_repair_service] +appraisal_services; [professional_services,appraisal_services] +architect; [professional_services,architect] +architectural_designer; [professional_services,architectural_designer] +art_restoration_service; [professional_services,art_restoration_service] +awning_supplier; [professional_services,awning_supplier] +bail_bonds_service; [professional_services,bail_bonds_service] +bike_repair_maintenance; [professional_services,bike_repair_maintenance] +billing_services; [professional_services,billing_services] +bookbinding; [professional_services,bookbinding] +bookkeeper; [professional_services,bookkeeper] +bus_rentals; [professional_services,bus_rentals] +business_consulting; [professional_services,business_consulting] +calligraphy; [professional_services,calligraphy] +car_broker; [professional_services,car_broker] +career_counseling; [professional_services,career_counseling] +carpet_dyeing; [professional_services,carpet_dyeing] +cemeteries; [professional_services,cemeteries] +certification_agency; [professional_services,certification_agency] +child_care_and_day_care; [professional_services,child_care_and_day_care] +day_care_preschool; [professional_services,child_care_and_day_care,day_care_preschool] +clock_repair_service; [professional_services,clock_repair_service] +commercial_printer; [professional_services,commercial_printer] +commercial_refrigeration; [professional_services,commercial_refrigeration] +commissioned_artist; [professional_services,commissioned_artist] +community_book_boxes; [professional_services,community_book_boxes] +community_gardens; [professional_services,community_gardens] +computer_hardware_company; [professional_services,computer_hardware_company] +courier_and_delivery_services; [professional_services,courier_and_delivery_services] +crane_services; [professional_services,crane_services] +customs_broker; [professional_services,customs_broker] +delegated_driver_service; [professional_services,delegated_driver_service] +diamond_dealer; [professional_services,diamond_dealer] +digitizing_services; [professional_services,digitizing_services] +donation_center; [professional_services,donation_center] +duplication_services; [professional_services,duplication_services] +e_commerce_service; [professional_services,e_commerce_service] +editorial_services; [professional_services,editorial_services] +elder_care_planning; [professional_services,elder_care_planning] +electrical_consultant; [professional_services,electrical_consultant] +electronics_repair_shop; [professional_services,electronics_repair_shop] +elevator_service; [professional_services,elevator_service] +emergency_service; [professional_services,emergency_service] +employment_agencies; [professional_services,employment_agencies] +temp_agency; [professional_services,employment_agencies,temp_agency] +engraving; [professional_services,engraving] +environmental_abatement_services; [professional_services,environmental_abatement_services] +environmental_testing; [professional_services,environmental_testing] +farm_equipment_repair_service; [professional_services,farm_equipment_repair_service] +feng_shui; [professional_services,feng_shui] +fingerprinting_service; [professional_services,fingerprinting_service] +food_and_beverage_consultant; [professional_services,food_and_beverage_consultant] +forestry_service; [professional_services,forestry_service] +fortune_telling_service; [professional_services,fortune_telling_service] +furniture_rental_service; [professional_services,furniture_rental_service] +furniture_repair; [professional_services,furniture_repair] +furniture_reupholstery; [professional_services,furniture_reupholstery] +genealogists; [professional_services,genealogists] +generator_installation_repair; [professional_services,generator_installation_repair] +goldsmith; [professional_services,goldsmith] +graphic_designer; [professional_services,graphic_designer] +gunsmith; [professional_services,gunsmith] +hazardous_waste_disposal; [professional_services,hazardous_waste_disposal] +hydraulic_repair_service; [professional_services,hydraulic_repair_service] +hydro_jetting; [professional_services,hydro_jetting] +ice_supplier; [professional_services,ice_supplier] +immigration_assistance_services; [professional_services,immigration_assistance_services] +indoor_landscaping; [professional_services,indoor_landscaping] +jewelry_repair_service; [professional_services,jewelry_repair_service] +junkyard; [professional_services,junkyard] +knife_sharpening; [professional_services,knife_sharpening] +laboratory; [professional_services,laboratory] +lawn_mower_repair_service; [professional_services,lawn_mower_repair_service] +life_coach; [professional_services,life_coach] +lottery_ticket; [professional_services,lottery_ticket] +machine_and_tool_rentals; [professional_services,machine_and_tool_rentals] +machine_shop; [professional_services,machine_shop] +mailbox_center; [professional_services,mailbox_center] +marketing_agency; [professional_services,marketing_agency] +matchmaker; [professional_services,matchmaker] +mediator; [professional_services,mediator] +metal_detector_services; [professional_services,metal_detector_services] +misting_system_services; [professional_services,misting_system_services] +mobility_equipment_services; [professional_services,mobility_equipment_services] +mooring_service; [professional_services,mooring_service] +music_production_services; [professional_services,music_production_services] +nanny_services; [professional_services,nanny_services] +notary_public; [professional_services,notary_public] +package_locker; [professional_services,package_locker] +packaging_contractors_and_service; [professional_services,packaging_contractors_and_service] +patent_law; [professional_services,patent_law] +payroll_services; [professional_services,payroll_services] +personal_assistant; [professional_services,personal_assistant] +pest_control_service; [professional_services,pest_control_service] +powder_coating_service; [professional_services,powder_coating_service] +printing_services; [professional_services,printing_services] +private_investigation; [professional_services,private_investigation] +product_design; [professional_services,product_design] +propane_supplier; [professional_services,propane_supplier] +public_adjuster; [professional_services,public_adjuster] +public_relations; [professional_services,public_relations] +record_label; [professional_services,record_label] +recording_and_rehearsal_studio; [professional_services,recording_and_rehearsal_studio] +recycling_center; [professional_services,recycling_center] +sandblasting_service; [professional_services,sandblasting_service] +screen_printing_t_shirt_printing; [professional_services,screen_printing_t_shirt_printing] +security_services; [professional_services,security_services] +septic_services; [professional_services,septic_services] +shipping_center; [professional_services,shipping_center] +shoe_repair; [professional_services,shoe_repair] +shoe_shining_service; [professional_services,shoe_shining_service] +shredding_services; [professional_services,shredding_services] +sign_making; [professional_services,sign_making] +snow_removal_service; [professional_services,snow_removal_service] +snuggle_service; [professional_services,snuggle_service] +software_development; [professional_services,software_development] +talent_agency; [professional_services,talent_agency] +taxidermist; [professional_services,taxidermist] +telephone_services; [professional_services,telephone_services] +tenant_and_eviction_law; [professional_services,tenant_and_eviction_law] +tent_house_supplier; [professional_services,tent_house_supplier] +translation_services; [professional_services,translation_services] +tv_mounting; [professional_services,tv_mounting] +typing_services; [professional_services,typing_services] +video_film_production; [professional_services,video_film_production] +watch_repair_service; [professional_services,watch_repair_service] +water_delivery; [professional_services,water_delivery] +web_designer; [professional_services,web_designer] +well_drilling; [professional_services,well_drilling] +wildlife_control; [professional_services,wildlife_control] +writing_service; [professional_services,writing_service] +storage_facility; [professional_services,storage_facility] +rv_and_boat_storage_facility; [professional_services,storage_facility,rv_and_boat_storage_facility] +boat_storage_facility; [professional_services,storage_facility,rv_and_boat_storage_facility,boat_storage_facility] +rv_storage_facility; [professional_services,storage_facility,rv_and_boat_storage_facility,rv_storage_facility] +self_storage_facility; [professional_services,storage_facility,self_storage_facility] +sewing_and_alterations; [professional_services,sewing_and_alterations] +gents_tailor; [professional_services,sewing_and_alterations,gents_tailor] +utility_service; [professional_services,utility_service] +electricity_supplier; [professional_services,utility_service,electricity_supplier] +garbage_collection_service; [professional_services,utility_service,garbage_collection_service] +natural_gas_supplier; [professional_services,utility_service,natural_gas_supplier] +public_phones; [professional_services,utility_service,public_phones] +public_restrooms; [professional_services,utility_service,public_restrooms] +water_supplier; [professional_services,utility_service,water_supplier] +structure_and_geography; [structure_and_geography] +bridge; [structure_and_geography,bridge] +canal; [structure_and_geography,canal] +dam; [structure_and_geography,dam] +desert; [structure_and_geography,desert] +forest; [structure_and_geography,forest] +geologic_formation; [structure_and_geography,geologic_formation] +island; [structure_and_geography,island] +mountain; [structure_and_geography,mountain] +natural_hot_springs; [structure_and_geography,natural_hot_springs] +nature_reserve; [structure_and_geography,nature_reserve] +pier; [structure_and_geography,pier] +public_plaza; [structure_and_geography,public_plaza] +quay; [structure_and_geography,quay] +river; [structure_and_geography,river] +skyscraper; [structure_and_geography,skyscraper] +tower; [structure_and_geography,tower] +weir; [structure_and_geography,weir] diff --git a/src/db/functions/classify_segment.sql b/src/db/functions/classify_segment.sql index c4d5143..856a80c 100644 --- a/src/db/functions/classify_segment.sql +++ b/src/db/functions/classify_segment.sql @@ -1,23 +1,19 @@ DROP TYPE IF EXISTS output_segment CASCADE; CREATE TYPE output_segment AS ( - id text, length_m float8, length_3857 float8, - osm_id int8, bicycle text, foot text, - class_ text, impedance_slope float8, - impedance_slope_reverse float8, - impedance_surface float8, - coordinates_3857 json, maxspeed_forward integer, - maxspeed_backward integer, "source" text, - source_index integer, target text, - target_index integer, tags jsonb, - geom public.geometry(linestring, 4326), + id text, overture_id text, length_m float8, length_3857 float8, + class_ text, subclass text, impedance_slope float8, impedance_slope_reverse float8, + impedance_surface float8, coordinates_3857 json, maxspeed_forward integer, + maxspeed_backward integer, "source" text, source_index integer, + target text, target_index integer, geom public.geometry(linestring, 4326), h3_3 integer, h3_6 integer ); -DROP FUNCTION IF EXISTS public.classify_segment; -CREATE OR REPLACE FUNCTION public.classify_segment( +DROP FUNCTION IF EXISTS basic.classify_segment; +CREATE OR REPLACE FUNCTION basic.classify_segment( segment_id TEXT, - cycling_surfaces JSONB + cycling_surfaces JSONB, + default_speed_limits JSONB ) RETURNS VOID AS $$ @@ -30,36 +26,58 @@ DECLARE sub_segments output_segment[] = '{}'; output_segments output_segment[] = '{}'; - source_conn_geom public.geometry(point, 4326); - target_conn_geom public.geometry(point, 4326); + source_conn_location float; + target_conn_location float; + + mph_kmph_conv_factor float = 1.60934; + car_modes text[] = ARRAY['vehicle', 'motor_vehicle', 'car']; + maxspeed_forward_list float[]; + maxspeed_backward_list float[]; + speed_limit jsonb; + restriction jsonb; + maxspeed_forward int; + maxspeed_backward int; BEGIN -- Select relevant input segment - SELECT id, subtype, connectors, geometry, - road::jsonb->>'class' AS class, - road::jsonb->'roadNames' AS roadNames, - road::jsonb->'surface' AS surface, - road::jsonb->'flags' AS flags, - road::jsonb->'restrictions' AS restrictions, - road::jsonb AS allData + SELECT + id, + subtype, + connectors::jsonb AS connectors, + geometry, + class, + subclass, + names::jsonb->>'primary' AS name, + road_surface::jsonb AS road_surface, + access_restrictions::jsonb AS access_restrictions, + speed_limits::jsonb AS speed_limits INTO input_segment FROM temporal.segments WHERE id = segment_id; + -- Skip this segment if the subtype is not road + IF input_segment.subtype != 'road' THEN + RETURN; + END IF; + -- Check if segment needs to be split into sub-segments - IF array_length(input_segment.connectors, 1) > 2 THEN + IF jsonb_array_length(input_segment.connectors) > 2 THEN -- Split segment into sub-segments - FOR i IN 2..array_length(input_segment.connectors, 1) LOOP + FOR i IN 1..(jsonb_array_length(input_segment.connectors) - 1) LOOP -- Initialize sub-segment primary properties new_sub_segment.id = input_segment.id || '_sub_' || i-1; - SELECT geometry INTO source_conn_geom FROM temporal.connectors WHERE id = input_segment.connectors[i-1]; - SELECT geometry INTO target_conn_geom FROM temporal.connectors WHERE id = input_segment.connectors[i]; + SELECT ST_LineLocatePoint(input_segment.geometry, geometry) INTO source_conn_location FROM temporal.connectors WHERE id = (input_segment.connectors[i-1]->>'connector_id'); + SELECT ST_LineLocatePoint(input_segment.geometry, geometry) INTO target_conn_location FROM temporal.connectors WHERE id = (input_segment.connectors[i]->>'connector_id'); + + -- Handle rare cases with invalid connector locations + CONTINUE WHEN source_conn_location > target_conn_location; + new_sub_segment.geom = ST_LineSubstring( input_segment.geometry, - ST_LineLocatePoint(input_segment.geometry, source_conn_geom), - ST_LineLocatePoint(input_segment.geometry, target_conn_geom) + source_conn_location, + target_conn_location ); - new_sub_segment.source = input_segment.connectors[i-1]; - new_sub_segment.target = input_segment.connectors[i]; + new_sub_segment.source = (input_segment.connectors[i-1]->>'connector_id'); + new_sub_segment.target = (input_segment.connectors[i]->>'connector_id'); -- TODO Handle linear split surface for sub-segment -- TODO Handle linear split speed limits for sub-segment @@ -71,8 +89,8 @@ BEGIN -- Initialize segment primary properties new_sub_segment.id = input_segment.id; new_sub_segment.geom = input_segment.geometry; - new_sub_segment.source = input_segment.connectors[1]; - new_sub_segment.target = input_segment.connectors[2]; + new_sub_segment.source = (input_segment.connectors[0]->>'connector_id'); + new_sub_segment.target = (input_segment.connectors[1]->>'connector_id'); -- TODO Handle linear split surface for segment -- TODO Handle linear split speed limits for segment @@ -80,54 +98,136 @@ BEGIN sub_segments = array_append(sub_segments, new_sub_segment); END IF; - + + -- Process speed limits + IF jsonb_array_length(input_segment.speed_limits) > 0 THEN + FOR speed_limit IN SELECT * FROM jsonb_array_elements(input_segment.speed_limits) LOOP + -- Speed limits may have directionality, check for this + IF speed_limit ? 'when' AND (speed_limit->'when') ? 'heading' THEN + IF ((speed_limit->'when')->>'heading') = 'forward' THEN + -- Speed limit applies in the forward direction only + IF ((speed_limit->'max_speed')->>'unit') = 'km/h' THEN + maxspeed_forward_list := array_append(maxspeed_forward_list, ((speed_limit->'max_speed')->>'value')::float); + ELSE + maxspeed_forward_list := array_append(maxspeed_forward_list, ((speed_limit->'max_speed')->>'value')::float * mph_kmph_conv_factor); + END IF; + ELSE + -- Speed limit applies in the backward direction only + IF ((speed_limit->'max_speed')->>'unit') = 'km/h' THEN + maxspeed_backward_list := array_append(maxspeed_backward_list, ((speed_limit->'max_speed')->>'value')::float); + ELSE + maxspeed_backward_list := array_append(maxspeed_backward_list, ((speed_limit->'max_speed')->>'value')::float * mph_kmph_conv_factor); + END IF; + END IF; + ELSE + -- Speed limit applies in both directions + IF ((speed_limit->'max_speed')->>'unit') = 'km/h' THEN + maxspeed_forward_list := array_append(maxspeed_forward_list, ((speed_limit->'max_speed')->>'value')::float); + maxspeed_backward_list := array_append(maxspeed_backward_list, ((speed_limit->'max_speed')->>'value')::float); + ELSE + maxspeed_forward_list := array_append(maxspeed_forward_list, ((speed_limit->'max_speed')->>'value')::float * mph_kmph_conv_factor); + maxspeed_backward_list := array_append(maxspeed_backward_list, ((speed_limit->'max_speed')->>'value')::float * mph_kmph_conv_factor); + END IF; + END IF; + END LOOP; + + SELECT round(avg(value))::int + INTO maxspeed_forward + FROM unnest(maxspeed_forward_list) AS value; + + SELECT round(avg(value))::int + INTO maxspeed_backward + FROM unnest(maxspeed_backward_list) AS value; + END IF; + + -- Set default speed limits if none were specified in the data + IF maxspeed_forward IS NULL THEN + maxspeed_forward = (default_speed_limits->>input_segment.class)::int; + END IF; + + IF maxspeed_backward IS NULL THEN + maxspeed_backward = (default_speed_limits->>input_segment.class)::int; + END IF; + + -- Process access restrictions + FOR restriction IN SELECT * FROM jsonb_array_elements(input_segment.access_restrictions) LOOP + IF restriction->>'access_type' = 'denied' THEN + -- Restrictions regarding access + IF (restriction->'when') IS NULL THEN + maxspeed_forward = NULL; + maxspeed_backward = NULL; + EXIT; + END IF; + + -- Restrictions regarding the direction of travel + IF (restriction->'when')->>'heading' = 'forward' THEN + maxspeed_forward = NULL; + ELSIF (restriction->'when')->>'heading' = 'backward' THEN + maxspeed_backward = NULL; + END IF; + + -- Restrictions regarding the type of vehicle + IF (restriction->'when')->'mode' ?| car_modes THEN + maxspeed_forward = NULL; + maxspeed_backward = NULL; + EXIT; + END IF; + -- Ignore these becuase they are not always defined consistently + -- ELSIF restriction->>'access_type' = 'allowed' THEN + -- Restrictions regarding the type of vehicle + -- IF (restriction->'when') ? 'mode' AND NOT (restriction->'when')->'mode' ?| car_modes THEN + -- maxspeed_forward = NULL; + -- maxspeed_backward = NULL; + -- EXIT; + -- END IF; + END IF; + END LOOP; + -- Clip sub-segments to fit into h3_3 and h3_6 cells - SELECT clip_segments(sub_segments, 6) INTO output_segments; - SELECT clip_segments(output_segments, 3) INTO output_segments; + SELECT basic.clip_segments(sub_segments, 6) INTO output_segments; + SELECT basic.clip_segments(output_segments, 3) INTO output_segments; -- Loop through final output segments FOREACH output_segment IN ARRAY output_segments LOOP -- Set remaining properties for every output segment, these are derived from primary properties + output_segment.overture_id = input_segment.id; output_segment.length_m = ST_Length(output_segment.geom::geography); output_segment.length_3857 = ST_Length(ST_Transform(output_segment.geom, 3857)); - output_segment.coordinates_3857 = (ST_AsGeoJson(ST_Transform(output_segment.geom, 3857))::jsonb)['coordinates']; - output_segment.osm_id = NULL; + output_segment.coordinates_3857 = ((ST_AsGeoJson(ST_Transform(output_segment.geom, 3857)))::jsonb)['coordinates']; output_segment.class_ = input_segment.class; - output_segment.h3_3 = to_short_h3_3(h3_lat_lng_to_cell(ST_Centroid(output_segment.geom)::point, 3)::bigint); - output_segment.h3_6 = to_short_h3_6(h3_lat_lng_to_cell(ST_Centroid(output_segment.geom)::point, 6)::bigint); + output_segment.subclass = input_segment.subclass; + output_segment.maxspeed_forward = maxspeed_forward; + output_segment.maxspeed_backward = maxspeed_backward; + output_segment.h3_3 = basic.to_short_h3_3(h3_lat_lng_to_cell(ST_Centroid(output_segment.geom)::point, 3)::bigint); + output_segment.h3_6 = basic.to_short_h3_6(h3_lat_lng_to_cell(ST_Centroid(output_segment.geom)::point, 6)::bigint); - -- Temporarily set the following properties here, but evetually handle linear split values above - IF jsonb_typeof(input_segment.surface) != 'array' THEN - output_segment.impedance_surface = (cycling_surfaces ->> (input_segment.allData ->> 'surface'))::float; + -- Drop this segment if it isn't within the bounds of the h3_3 and h3_6 grids + IF NOT EXISTS (SELECT 1 FROM basic.h3_6_grid WHERE h3_short = output_segment.h3_6) + OR NOT EXISTS (SELECT 1 FROM basic.h3_3_grid WHERE h3_short = output_segment.h3_3) THEN + CONTINUE; END IF; - output_segment.maxspeed_forward = ((input_segment.restrictions -> 'speedLimits') -> 'maxSpeed')[0]; - output_segment.tags = input_segment.flags; - -- Check if digital elevation model (DEM) table exists and compute impedance values - -- IF EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'dem') THEN - -- SELECT c.* - -- INTO output_segment.impedance_slope, output_segment.impedance_slope_reverse - -- FROM get_slope_profile(output_segment.geom, output_segment.length_m, ST_LENGTH(output_segment.geom)) s, - -- LATERAL compute_impedances(s.elevs, s.linklength, s.lengthinterval) c; - -- END IF; + -- Temporarily set the following properties here, but eventually handle linear split values above + IF jsonb_array_length(input_segment.road_surface) > 0 THEN + output_segment.impedance_surface = (cycling_surfaces ->> (input_segment.road_surface[0]->>'value'))::float; + END IF; -- Insert processed output segment data into table INSERT INTO basic.segment ( - length_m, length_3857, - osm_id, bicycle, foot, - class_, impedance_slope, impedance_slope_reverse, + overture_id, length_m, length_3857, class_, + subclass, impedance_slope, impedance_slope_reverse, impedance_surface, coordinates_3857, maxspeed_forward, - maxspeed_backward, source, target, - tags, geom, h3_3, h3_6 + maxspeed_backward, source, target, geom, h3_3, h3_6 ) VALUES ( - output_segment.length_m, output_segment.length_3857, - output_segment.osm_id, output_segment.bicycle, output_segment.foot, - output_segment.class_, output_segment.impedance_slope, output_segment.impedance_slope_reverse, + output_segment.overture_id, output_segment.length_m, output_segment.length_3857, output_segment.class_, + output_segment.subclass, output_segment.impedance_slope, output_segment.impedance_slope_reverse, output_segment.impedance_surface, output_segment.coordinates_3857, output_segment.maxspeed_forward, output_segment.maxspeed_backward, output_segment.source_index, output_segment.target_index, - output_segment.tags, output_segment.geom, output_segment.h3_3, output_segment.h3_6 + output_segment.geom, output_segment.h3_3, output_segment.h3_6 ); END LOOP; END -$$ LANGUAGE plpgsql; +$$ +LANGUAGE plpgsql +PARALLEL SAFE; diff --git a/src/db/functions/clip_segments.sql b/src/db/functions/clip_segments.sql index 57ae6d2..4c7b8e4 100644 --- a/src/db/functions/clip_segments.sql +++ b/src/db/functions/clip_segments.sql @@ -1,5 +1,5 @@ -DROP FUNCTION IF EXISTS public.clip_segments; -CREATE OR REPLACE FUNCTION public.clip_segments( +DROP FUNCTION IF EXISTS basic.clip_segments; +CREATE OR REPLACE FUNCTION basic.clip_segments( input_segments output_segment[], h3_resolution integer ) @@ -34,27 +34,27 @@ FOREACH new_sub_segment IN ARRAY input_segments LOOP -- Splitting this segment is not necessary, just process its source/target connectors -- If source connector doesn't already exist in output table, insert it - INSERT INTO basic.connector (id, osm_id, geom, h3_3, h3_6) - SELECT id, NULL, geometry, to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), - to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) + INSERT INTO basic.connector (overture_id, geom, h3_3, h3_6) + SELECT id, geometry, basic.to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), + basic.to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) FROM temporal.connectors WHERE id = new_sub_segment.source ON CONFLICT DO NOTHING; -- Get serial index of new or existing source connector - SELECT index FROM basic.connector WHERE id = new_sub_segment.source + SELECT id FROM basic.connector WHERE overture_id = new_sub_segment.source INTO new_sub_segment.source_index; -- If target connector doesn't already exist in output table, insert it - INSERT INTO basic.connector (id, osm_id, geom, h3_3, h3_6) - SELECT id, NULL, geometry, to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), - to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) + INSERT INTO basic.connector (overture_id, geom, h3_3, h3_6) + SELECT id, geometry, basic.to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), + basic.to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) FROM temporal.connectors WHERE id = new_sub_segment.target ON CONFLICT DO NOTHING; -- Get serial index of new or existing target connector - SELECT index FROM basic.connector WHERE id = new_sub_segment.target + SELECT id FROM basic.connector WHERE overture_id = new_sub_segment.target INTO new_sub_segment.target_index; output_segments = array_append(output_segments, new_sub_segment); @@ -80,73 +80,71 @@ FOREACH new_sub_segment IN ARRAY input_segments LOOP output_segment.id = new_sub_segment.id || '_clip_' || split_geometry.row_index; output_segment.geom = split_geometry.geom; output_segment.impedance_surface = new_sub_segment.impedance_surface; - output_segment.maxspeed_forward = new_sub_segment.maxspeed_forward; - output_segment.tags = new_sub_segment.tags; IF split_geometry.row_index = 1 THEN output_segment.source = new_sub_segment.source; output_segment.target = 'connector.' || output_segment.id; -- If source connector doesn't already exist in output table, insert it - INSERT INTO basic.connector (id, osm_id, geom, h3_3, h3_6) - SELECT id, NULL, geometry, to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), - to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) + INSERT INTO basic.connector (overture_id, geom, h3_3, h3_6) + SELECT id, geometry, basic.to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), + basic.to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) FROM temporal.connectors WHERE id = output_segment.source ON CONFLICT DO NOTHING; -- Get serial index of new or existing source connector - SELECT index FROM basic.connector WHERE id = output_segment.source + SELECT id FROM basic.connector WHERE overture_id = output_segment.source INTO output_segment.source_index; -- Create new target connector for split segment - INSERT INTO basic.connector (id, osm_id, geom, h3_3, h3_6) + INSERT INTO basic.connector (overture_id, geom, h3_3, h3_6) VALUES ( - output_segment.target, NULL, split_geometry.target, - to_short_h3_3(h3_lat_lng_to_cell(split_geometry.target::point, 3)::bigint), - to_short_h3_6(h3_lat_lng_to_cell(split_geometry.target::point, 6)::bigint) + output_segment.target, split_geometry.target, + basic.to_short_h3_3(h3_lat_lng_to_cell(split_geometry.target::point, 3)::bigint), + basic.to_short_h3_6(h3_lat_lng_to_cell(split_geometry.target::point, 6)::bigint) ); -- Get serial index of new target connector - SELECT index FROM basic.connector WHERE id = output_segment.target + SELECT id FROM basic.connector WHERE overture_id = output_segment.target INTO output_segment.target_index; ELSIF split_geometry.row_index > 1 AND split_geometry.row_index < split_geometry.row_count THEN output_segment.source = 'connector.' || new_sub_segment.id || '_clip_' || (split_geometry.row_index - 1); output_segment.target = 'connector.' || output_segment.id; -- Get serial index of source connector created by previous split segment - SELECT index FROM basic.connector WHERE id = output_segment.source + SELECT id FROM basic.connector WHERE overture_id = output_segment.source INTO output_segment.source_index; -- Create new target connector for split segment - INSERT INTO basic.connector (id, osm_id, geom, h3_3, h3_6) + INSERT INTO basic.connector (overture_id, geom, h3_3, h3_6) VALUES ( - output_segment.target, NULL, split_geometry.target, - to_short_h3_3(h3_lat_lng_to_cell(split_geometry.target::point, 3)::bigint), - to_short_h3_6(h3_lat_lng_to_cell(split_geometry.target::point, 6)::bigint) + output_segment.target, split_geometry.target, + basic.to_short_h3_3(h3_lat_lng_to_cell(split_geometry.target::point, 3)::bigint), + basic.to_short_h3_6(h3_lat_lng_to_cell(split_geometry.target::point, 6)::bigint) ); -- Get serial index of new target connector - SELECT index FROM basic.connector WHERE id = output_segment.target + SELECT id FROM basic.connector WHERE overture_id = output_segment.target INTO output_segment.target_index; ELSE output_segment.source = 'connector.' || new_sub_segment.id || '_clip_' || (split_geometry.row_index - 1); output_segment.target = new_sub_segment.target; -- Get serial index of source connector created by previous split segment - SELECT index FROM basic.connector WHERE id = output_segment.source + SELECT id FROM basic.connector WHERE overture_id = output_segment.source INTO output_segment.source_index; -- If target connector doesn't already exist in output table, insert it - INSERT INTO basic.connector (id, osm_id, geom, h3_3, h3_6) - SELECT id, NULL, geometry, to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), - to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) + INSERT INTO basic.connector (overture_id, geom, h3_3, h3_6) + SELECT id, geometry, basic.to_short_h3_3(h3_lat_lng_to_cell(geometry::point, 3)::bigint), + basic.to_short_h3_6(h3_lat_lng_to_cell(geometry::point, 6)::bigint) FROM temporal.connectors WHERE id = output_segment.target ON CONFLICT DO NOTHING; -- Get serial index of new or existing target connector - SELECT index FROM basic.connector WHERE id = output_segment.target + SELECT id FROM basic.connector WHERE overture_id = output_segment.target INTO output_segment.target_index; END IF; diff --git a/src/db/functions/compute_impedances.sql b/src/db/functions/compute_impedances.sql index a38ff6c..dcb5104 100755 --- a/src/db/functions/compute_impedances.sql +++ b/src/db/functions/compute_impedances.sql @@ -1,5 +1,5 @@ -DROP FUNCTION IF EXISTS public.compute_impedances; -CREATE OR REPLACE FUNCTION public.compute_impedances(elevs float[], linkLength float, lengthInterval float) +DROP FUNCTION IF EXISTS basic.compute_impedances; +CREATE OR REPLACE FUNCTION basic.compute_impedances(elevs float[], linkLength float, lengthInterval float) RETURNS TABLE (imp float, rs_imp float, avg_slope integer) LANGUAGE plpgsql AS $function$ @@ -82,11 +82,11 @@ $function$ IMMUTABLE; /* EXPLAIN ANALYZE -select compute_impedances(ARRAY[592.7148121482129,592.9108133538796,592.8564161772507,592.9966675210214],25,10) +select basic.compute_impedances(ARRAY[592.7148121482129,592.9108133538796,592.8564161772507,592.9966675210214],25,10) EXPLAIN ANALYZE SELECT * -FROM compute_impedances(ARRAY[592.7148121482129,592.9108133538796,592.8564161772507,592.9966675210214,593.5142995351175,593.7934132187218,594.0791123343638,594.4123340711714,595.0709339933488,595.297176661014,595.5693572768498,595.9848412527108,596.8401692540409,597.3171319612385,597.3482358264915,597.5382529434738,597.8022676643865,597.8740809185631,597.823075664003,597.7863504450027,597.626674683176,597.6343312386101,597.4277702839657,597.4008755308755,595.8668147549672,595.8660641137426,595.7965538931242,595.6454700224763,595.0920134259331,594.637849977284,594.7157834949176,594.8803722155301,594.9887926115971,594.9830892260971,594.9870887111837,595.0787212146299,595.0386387688128,595.0324550215119,594.8250819105949,594.7880532571487,594.8995285649445,594.8810906098278,594.6393061703341,594.5495462993056,594.659993488184,594.9117694793237,594.8561671328405,595.1014832310566,595.9805443687969,596.2352886423453,596.6661575732143,596.8519273771171,596.9980204380629,597.5609773647477,597.5791774155083,597.4665069723288,597.6586808056799,597.485007468097,597.2002620872586,597.1963692359632,597.2027716376099,597.3514613004011,597.5291349479928,597.7423810451334,597.8229651815739,598.3487641240755,598.9325624511746,599.1410746685413,599.5569045340294,599.6335839517587,600.0134635692586,600.0129365392264,600.182788759895,600.2291691233494,600.29322494889,600.5026235315236,600.588326757217,600.7822175536718,600.7889289583835,600.8583938103352,600.8506023673,600.8594569857722,600.855095798295,600.8699011121003,600.8948367881649,600.9158160563479,600.9308420352094,600.9317041421716,601.0112494866629,601.1194077913335,601.1447172081055,601.2190570375012,601.1755003536814,601.2574300612171,601.4244977205623,601.3280721278472,601.4095292607226,601.4469409424296,601.343675456267,601.2644166539546,601.448225363388,601.764308894998,601.9141684209388,601.9285510317757,602.2221844031513,602.927047984513,603.209684715867,603.4120463058216,603.609247115536,604.0931344921927,604.3979434665107,604.0595104609955,604.1208265697768,604.4242234358535,604.2729929211542,604.1726212874402,604.0363343203236,603.9131865304082,603.7376785831837,603.6344507785851,603.1815415276134,603.021235237828,602.8921379501912,602.8012998289823,602.8292367257493,602.8971681366721,602.9116349735643,602.8851462845998,602.8320536308945,602.8306007032874,602.8344996324383,602.8328210595299,603.0523886491386,603.165058943027,603.0310286699672,602.9595157706631,602.9181726332915]::FLOAT[], +FROM basic.compute_impedances(ARRAY[592.7148121482129,592.9108133538796,592.8564161772507,592.9966675210214,593.5142995351175,593.7934132187218,594.0791123343638,594.4123340711714,595.0709339933488,595.297176661014,595.5693572768498,595.9848412527108,596.8401692540409,597.3171319612385,597.3482358264915,597.5382529434738,597.8022676643865,597.8740809185631,597.823075664003,597.7863504450027,597.626674683176,597.6343312386101,597.4277702839657,597.4008755308755,595.8668147549672,595.8660641137426,595.7965538931242,595.6454700224763,595.0920134259331,594.637849977284,594.7157834949176,594.8803722155301,594.9887926115971,594.9830892260971,594.9870887111837,595.0787212146299,595.0386387688128,595.0324550215119,594.8250819105949,594.7880532571487,594.8995285649445,594.8810906098278,594.6393061703341,594.5495462993056,594.659993488184,594.9117694793237,594.8561671328405,595.1014832310566,595.9805443687969,596.2352886423453,596.6661575732143,596.8519273771171,596.9980204380629,597.5609773647477,597.5791774155083,597.4665069723288,597.6586808056799,597.485007468097,597.2002620872586,597.1963692359632,597.2027716376099,597.3514613004011,597.5291349479928,597.7423810451334,597.8229651815739,598.3487641240755,598.9325624511746,599.1410746685413,599.5569045340294,599.6335839517587,600.0134635692586,600.0129365392264,600.182788759895,600.2291691233494,600.29322494889,600.5026235315236,600.588326757217,600.7822175536718,600.7889289583835,600.8583938103352,600.8506023673,600.8594569857722,600.855095798295,600.8699011121003,600.8948367881649,600.9158160563479,600.9308420352094,600.9317041421716,601.0112494866629,601.1194077913335,601.1447172081055,601.2190570375012,601.1755003536814,601.2574300612171,601.4244977205623,601.3280721278472,601.4095292607226,601.4469409424296,601.343675456267,601.2644166539546,601.448225363388,601.764308894998,601.9141684209388,601.9285510317757,602.2221844031513,602.927047984513,603.209684715867,603.4120463058216,603.609247115536,604.0931344921927,604.3979434665107,604.0595104609955,604.1208265697768,604.4242234358535,604.2729929211542,604.1726212874402,604.0363343203236,603.9131865304082,603.7376785831837,603.6344507785851,603.1815415276134,603.021235237828,602.8921379501912,602.8012998289823,602.8292367257493,602.8971681366721,602.9116349735643,602.8851462845998,602.8320536308945,602.8306007032874,602.8344996324383,602.8328210595299,603.0523886491386,603.165058943027,603.0310286699672,602.9595157706631,602.9181726332915]::FLOAT[], 1353.3649483199176, 10.0); */ diff --git a/src/db/functions/divide_polygon.sql b/src/db/functions/divide_polygon.sql new file mode 100644 index 0000000..8be5e0d --- /dev/null +++ b/src/db/functions/divide_polygon.sql @@ -0,0 +1,54 @@ +-- DROP FUNCTION basic.divide_polygon(geometry, int4, int4); + +CREATE OR REPLACE FUNCTION basic.divide_polygon(input_geom geometry, num_cells_x integer, num_cells_y integer) + RETURNS TABLE(geom geometry) + LANGUAGE plpgsql +AS $function$ +BEGIN + RETURN QUERY + WITH + bounds AS ( + SELECT + ST_XMin(ST_Envelope(input_geom)) AS xmin, + ST_XMax(ST_Envelope(input_geom)) AS xmax, + ST_YMin(ST_Envelope(input_geom)) AS ymin, + ST_YMax(ST_Envelope(input_geom)) AS ymax + ), + x_splits AS ( + SELECT + generate_series(1, num_cells_x) AS part, + ST_XMin(input_geom) + (generate_series(1, num_cells_x) - 1) * ((ST_XMax(input_geom) - ST_XMin(input_geom)) / num_cells_x) AS x_start, + ST_XMin(input_geom) + generate_series(1, num_cells_x) * ((ST_XMax(input_geom) - ST_XMin(input_geom)) / num_cells_x) AS x_end + FROM bounds + ), + y_splits AS ( + SELECT + generate_series(1, num_cells_y) AS part, + ST_YMin(input_geom) + (generate_series(1, num_cells_y) - 1) * ((ST_YMax(input_geom) - ST_YMin(input_geom)) / num_cells_y) AS y_start, + ST_YMin(input_geom) + generate_series(1, num_cells_y) * ((ST_YMax(input_geom) - ST_YMin(input_geom)) / num_cells_y) AS y_end + FROM bounds + ), + grid AS ( + SELECT + ST_MakePolygon( + ST_GeomFromText( + FORMAT( + 'LINESTRING(%s %s, %s %s, %s %s, %s %s, %s %s)', + x_splits.x_start, y_splits.y_start, + x_splits.x_start, y_splits.y_end, + x_splits.x_end, y_splits.y_end, + x_splits.x_end, y_splits.y_start, + x_splits.x_start, y_splits.y_start + ), + 4326 + ) + ) AS cell + FROM x_splits, y_splits + ) + SELECT + ST_Intersection(input_geom, cell) AS geom + FROM + grid; +END; +$function$ +; diff --git a/src/db/functions/fill_polygon_h3.sql b/src/db/functions/fill_polygon_h3.sql index a1e1c55..d7f0094 100644 --- a/src/db/functions/fill_polygon_h3.sql +++ b/src/db/functions/fill_polygon_h3.sql @@ -1,6 +1,6 @@ /*This function returns the h3 indexes that are intersecting the borderpoints of a specified geometry*/ -DROP FUNCTION IF EXISTS public.fill_polygon_h3; -CREATE OR REPLACE FUNCTION public.fill_polygon_h3(geom geometry, h3_resolution integer) +DROP FUNCTION IF EXISTS basic.fill_polygon_h3; +CREATE OR REPLACE FUNCTION basic.fill_polygon_h3(geom geometry, h3_resolution integer) RETURNS TABLE (h3_index h3index, h3_boundary geometry(linestring, 4326), h3_geom geometry(polygon, 4326)) LANGUAGE plpgsql AS $function$ diff --git a/src/db/functions/get_idw_values.sql b/src/db/functions/get_idw_values.sql index bec33cf..e89053a 100755 --- a/src/db/functions/get_idw_values.sql +++ b/src/db/functions/get_idw_values.sql @@ -1,7 +1,7 @@ BEGIN; SET LOCAL check_function_bodies TO FALSE; -DROP FUNCTION IF EXISTS public.get_idw_values; -CREATE OR REPLACE FUNCTION public.get_idw_values(geom geometry, buffer_distance float) +DROP FUNCTION IF EXISTS basic.get_idw_values; +CREATE OR REPLACE FUNCTION basic.get_idw_values(geom geometry, buffer_distance float) RETURNS TABLE (dp_geom geometry, distance float, val float) LANGUAGE sql AS $function$ @@ -13,6 +13,7 @@ AS $function$ WHERE d.rast && st_buffer(geom, buffer_distance) ) r , LATERAL ST_PixelAsCentroids(rast, 1) AS dp + WHERE ST_DISTANCE(r.geom, dp.geom) != 0 ORDER BY r.geom <-> dp.geom LIMIT 3 diff --git a/src/db/functions/get_slope_profile.sql b/src/db/functions/get_slope_profile.sql index a24ad3a..8dda50e 100755 --- a/src/db/functions/get_slope_profile.sql +++ b/src/db/functions/get_slope_profile.sql @@ -1,5 +1,5 @@ -DROP FUNCTION IF EXISTS public.get_slope_profile; -CREATE OR REPLACE FUNCTION public.get_slope_profile(way_geom geometry, length_meters float, length_degree float, interval_ float default 10, dem_resolution float default 30) +DROP FUNCTION IF EXISTS basic.get_slope_profile; +CREATE OR REPLACE FUNCTION basic.get_slope_profile(way_geom geometry, length_meters float, length_degree float, interval_ float default 10, dem_resolution float default 30) RETURNS TABLE(elevs float[], linkLength float, lengthInterval float) LANGUAGE plpgsql AS $function$ @@ -7,6 +7,10 @@ DECLARE translation_m_degree NUMERIC; dump_points geometry[]; BEGIN + IF length_meters = 0 THEN + length_meters = 1; + END IF; + translation_m_degree = length_degree/length_meters; dem_resolution = dem_resolution * translation_m_degree; @@ -38,7 +42,7 @@ BEGIN FROM ( SELECT SUM(idw.val/(idw.distance/translation_m_degree))/SUM(1/(idw.distance/translation_m_degree))::real AS elev - FROM points p, get_idw_values(geom, dem_resolution) idw + FROM points p, basic.get_idw_values(geom, dem_resolution) idw WHERE p.geom IS NOT NULL GROUP BY cnt ORDER BY cnt @@ -50,6 +54,6 @@ $function$; /* EXPLAIN ANALYZE SELECT s.* -FROM ways, LATERAL get_slope_profile(the_geom, length_m, length) s +FROM ways, LATERAL basic.get_slope_profile(the_geom, length_m, length) s LIMIT 1 */ \ No newline at end of file diff --git a/src/db/functions/to_short_h3_3.sql b/src/db/functions/to_short_h3_3.sql index 54f616b..9e6f55c 100644 --- a/src/db/functions/to_short_h3_3.sql +++ b/src/db/functions/to_short_h3_3.sql @@ -1,7 +1,6 @@ /*From https://github.com/igor-suhorukov/openstreetmap_h3*/ -DROP FUNCTION IF EXISTS to_short_h3_3; -CREATE FUNCTION to_short_h3_3(bigint) RETURNS integer -AS $$ select ($1 & 'x000ffff000000000'::bit(64)::bigint>>36)::integer;$$ +CREATE OR REPLACE FUNCTION basic.to_short_h3_3(bigint) RETURNS integer +AS $$ select ($1 & 'x000ffff000000000'::bit(64)::bigint>>36)::int;$$ LANGUAGE SQL IMMUTABLE -RETURNS NULL ON NULL INPUT; \ No newline at end of file +RETURNS NULL ON NULL INPUT; diff --git a/src/db/functions/to_short_h3_6.sql b/src/db/functions/to_short_h3_6.sql index 8b669ac..09d5ecf 100644 --- a/src/db/functions/to_short_h3_6.sql +++ b/src/db/functions/to_short_h3_6.sql @@ -1,7 +1,6 @@ /*Adaptation of https://github.com/igor-suhorukov/openstreetmap_h3*/ -DROP FUNCTION IF EXISTS to_short_h3_6; -CREATE FUNCTION to_short_h3_6(bigint) RETURNS int +CREATE OR REPLACE FUNCTION basic.to_short_h3_6(bigint) RETURNS integer AS $$ select ($1 & 'x000fffffff000000'::bit(64)::bigint>>24)::bit(32)::int;$$ LANGUAGE SQL IMMUTABLE -RETURNS NULL ON NULL INPUT; \ No newline at end of file +RETURNS NULL ON NULL INPUT; diff --git a/src/db/tables/gtfs.py b/src/db/tables/gtfs.py index 29d4687..ab0ae7f 100644 --- a/src/db/tables/gtfs.py +++ b/src/db/tables/gtfs.py @@ -3,6 +3,8 @@ def __init__(self, schema): self.schema = schema def sql_create_table(self) -> dict: + """SQL queries to create GTFS tables.""" + sql_create_table_agency = f""" CREATE TABLE {self.schema}.agency ( agency_id text NOT NULL, @@ -126,3 +128,57 @@ def sql_create_table(self) -> dict: "calendar_dates": sql_create_table_calendar_dates, "shapes": sql_create_table_shapes } + + def sql_select_table(self) -> dict: + """SQL queries to select data from GTFS tables.""" + + sql_select_table_agency = f""" + SELECT agency_id, agency_name, agency_url, agency_timezone, agency_lang, agency_phone, agency_fare_url + FROM {self.schema}.agency + """ + + sql_select_table_stops = f""" + SELECT stop_name, parent_station, stop_code, zone_id, stop_id, stop_desc, stop_lat, stop_lon,stop_url, location_type, stop_timezone, wheelchair_boarding, level_id, platform_code + FROM {self.schema}.stops + """ + + sql_select_table_routes = f""" + SELECT route_long_name, route_short_name, agency_id, route_desc, route_type, route_id, route_color, route_text_color, route_sort_order + FROM {self.schema}.routes + """ + + sql_select_table_trips = f""" + SELECT route_id, service_id, trip_headsign, trip_short_name, direction_id, block_id, shape_id, trip_id, wheelchair_accessible, bikes_allowed + FROM {self.schema}.trips + """ + + sql_select_table_stop_times = f""" + SELECT trip_id, arrival_time::text, departure_time::text, stop_id, stop_sequence, stop_headsign, pickup_type, drop_off_type, shape_dist_traveled, timepoint + FROM {self.schema}.stop_times + """ + + sql_select_table_calendar = f""" + SELECT monday, tuesday, wednesday, thursday, friday, saturday, sunday, REPLACE(start_date::text, '-', '') as start_date, REPLACE(end_date::text, '-', '') as end_date, service_id + FROM {self.schema}.calendar + """ + + sql_select_table_calendar_dates = f""" + SELECT service_id, exception_type, REPLACE(date::text, '-', '') as date + FROM {self.schema}.calendar_dates + """ + + sql_select_table_shapes = f""" + SELECT shape_id, shape_pt_lat, shape_pt_lon, shape_pt_sequence + FROM {self.schema}.shapes + """ + + return { + "agency": sql_select_table_agency, + "stops": sql_select_table_stops, + "routes": sql_select_table_routes, + "trips": sql_select_table_trips, + "stop_times": sql_select_table_stop_times, + "calendar": sql_select_table_calendar, + "calendar_dates": sql_select_table_calendar_dates, + "shapes": sql_select_table_shapes + } diff --git a/src/export/gtfs.py b/src/export/gtfs.py new file mode 100644 index 0000000..ac6803d --- /dev/null +++ b/src/export/gtfs.py @@ -0,0 +1,119 @@ +import csv +import os +import shutil + +from src.config.config import Config +from src.core.config import settings +from src.db.db import Database +from src.db.tables.gtfs import GtfsTables +from src.utils.utils import delete_file, make_dir, print_info + + +class GTFSExport: + def __init__(self, db: Database, region: str): + self.db = db + self.region = region + self.config = Config("gtfs", region) + self.schema = self.config.export["local_gtfs_schema"] + + # Create tables + gtfs_tables = GtfsTables(self.schema) + self.create_queries = gtfs_tables.sql_create_table() + self.select_queries = gtfs_tables.sql_select_table() + + # Output directory + self.output_dir = os.path.join(settings.OUTPUT_DATA_DIR, "gtfs", region) + + def initialize_output_file(self, table_name: str): + """Initialize an output file for a GTFS table.""" + + file_path = os.path.join(self.output_dir, f"{table_name}.txt") + + # Delete any existing output file + delete_file(file_path) + + # Create the output directory if it does not exist + make_dir(self.output_dir) + + # Create the output file + with open(file_path, 'w', newline='') as f: + pass + + print_info(f"Initialized output file for table {table_name}.") + + def write_to_output_file(self, table_name: str, h3_3: int=None, overwrite: bool=True): + """Write a GTFS table to an initialized output file.""" + + db_cursor = self.db.cursor() + + # Fetch table data + sql_query = self.select_queries[table_name] + if h3_3: + sql_query = f"{sql_query} WHERE h3_3 = {h3_3}" + db_cursor.execute(sql_query) + table_data = db_cursor.fetchall() + + # Fetch column names + columns = [desc[0] for desc in db_cursor.description] + + # Write the table data to a .txt file + with open(os.path.join(self.output_dir, f"{table_name}.txt"), 'w' if overwrite else 'a', newline='') as f: + writer = csv.writer(f) + if overwrite: + writer.writerow(columns) + writer.writerows(table_data) + + db_cursor.close() + + def compress_output_files(self): + """Compress the output files into a single .zip file.""" + + print_info("Compressing output files.") + os.execvp('zip', ['zip', '-r', '-j', os.path.join(self.output_dir, f"gtfs_{self.region}.zip"), self.output_dir]) + + def run(self): + """Run the gtfs export.""" + + # Warn the user if the output directory already contains a .zip file + if os.path.exists(os.path.join(self.output_dir, f"gtfs_{self.region}.zip")): + print_info("Warning: The output directory already contains a .zip file. This file will be deleted.") + delete_file(os.path.join(self.output_dir, f"gtfs_{self.region}.zip")) + + for table in self.create_queries: + # Initialize the output file + self.initialize_output_file(table) + + # Write table data to the output file + print_info(f"Exporting table {table}.") + + if table not in ["shapes", "stop_times"]: + self.write_to_output_file(table) + else: + # Fetch a list of h3_3 cells + h3_3_list = self.db.select(f""" + SELECT DISTINCT h3_3 + FROM {self.schema}.stop_times; + """) + h3_3_list = [val[0] for val in h3_3_list] + + # Write table data for each h3_3 cell + self.write_to_output_file(table, h3_3=h3_3_list[0]) + for h3_3 in h3_3_list[1:]: + self.write_to_output_file(table, h3_3=h3_3, overwrite=False) + + # Compress the output files + self.compress_output_files() + +def export_gtfs(region: str): + print_info(f"Export GTFS data for the region {region}.") + db = Database(settings.LOCAL_DATABASE_URI) + + try: + GTFSExport(db=db, region=region).run() + db.close() + print_info("Finished GTFS export.") + except Exception as e: + print(e) + raise e + finally: + db.close() diff --git a/src/preparation/gtfs.py b/src/preparation/gtfs.py index ad0c867..bd1dd4f 100644 --- a/src/preparation/gtfs.py +++ b/src/preparation/gtfs.py @@ -67,7 +67,7 @@ def prepare_shape_dist_region(self): ), h3_ids AS ( - SELECT DISTINCT to_short_h3_3(h3_lat_lng_to_cell(geom, 3)::bigint) AS h3_3, + SELECT DISTINCT basic.to_short_h3_3(h3_lat_lng_to_cell(geom, 3)::bigint) AS h3_3, ST_SETSRID(h3_cell_to_boundary(h3_lat_lng_to_cell(geom, 3))::geometry, 4326) AS geom FROM border_points ) @@ -520,11 +520,3 @@ def prepare_gtfs(region: str): raise e finally: db.close() - - -def export_gtfs(): - db = Database(settings.LOCAL_DATABASE_URI) - create_table_dump(db.db_config, "gtfs", "stops") - create_table_dump(db.db_config, "gtfs", "stop_times_optimized") - create_table_dump(db.db_config, "gtfs", "shape_dist_region") - db.close() diff --git a/src/preparation/network_pt.py b/src/preparation/network_pt.py old mode 100755 new mode 100644 index 724c3c8..4c13982 --- a/src/preparation/network_pt.py +++ b/src/preparation/network_pt.py @@ -1,208 +1,334 @@ import os -import time - -import requests from src.config.config import Config from src.core.config import settings from src.db.db import Database - -R5_FRONTEND_URL_REGIONS = f"{settings.R5_FRONTEND_HOST}:{settings.R5_FRONTEND_PORT}/api/db/regions" -R5_BACKEND_URL_BUNDLE = f"{settings.R5_BACKEND_HOST}:{settings.R5_BACKEND_PORT}/api/bundle" +from src.utils.utils import ( + delete_file, + make_dir, + osm_convert, + osm_crop_to_polygon, + osm_filter_to_highways, + osm_generate_polygon, + print_error, + print_info, + timing, +) class NetworkPTPreparation: - """Class to upload processed GTFS & OSM data to our R5 routing engine""" - - def __init__(self, db_rd, config, region): - self.db_rd = db_rd + """Class to process and clip OSM and GTFS data into sub-regions.""" + + def __init__(self, db: Database, db_rd: Database, region: str): self.region = region - self.sub_regions = self.db_rd.select(config.get("sub_regions_query")) - - self.sub_region_gtfs_input_dir = os.path.join(settings.INPUT_DATA_DIR, "network_pt", region) - self.sub_region_osm_output_dir = os.path.join(settings.OUTPUT_DATA_DIR, "network_pt", region) - - self.headers = {} - if settings.R5_AUTHORIZATION: - self.headers["Authorization"] = settings.R5_AUTHORIZATION - - - def upload_processed_data(self): - """Creates network bundles for every sub-region to upload OSM & GTFS data""" - + self.db = db + self.db_rd = db_rd + self.config = Config("network_pt", region) + + self.input_dir = os.path.join(settings.INPUT_DATA_DIR, "network_pt", region) + self.output_dir = os.path.join(settings.OUTPUT_DATA_DIR, "network_pt", region) + self.local_sub_region_table = self.config.preparation["local_sub_region_table"] + + self.sub_regions = None + + def create_sub_regions(self): + """Create sub-regions based on the region geometry and grid size specified.""" + + sub_region_buffer_dist = self.config.preparation["sub_region_buffer_dist"] + sub_region_grid_size = self.config.preparation["sub_region_count"] + sub_region_grid_width = int(sub_region_grid_size / 2) + sub_region_grid_height = int(sub_region_grid_size / 2) + if sub_region_grid_size % 2 != 0: + sub_region_grid_height += 1 + + print_info(f"Generating sub-regions for region: {self.region}") + + # Fetch region geometry + region_geom = self.db_rd.select(f""" + SELECT ST_AsText(geom) as geom + FROM ({self.config.preparation["region"]}) AS region; + """)[0][0] + + # Divide region geometry into sub-regions + sql_create_table = f""" + DROP TABLE IF EXISTS {self.local_sub_region_table}; + CREATE TABLE {self.local_sub_region_table} AS + WITH region AS ( + SELECT ST_GeomFromText('{region_geom}', 4326) AS geom + ) + SELECT + ROW_NUMBER() OVER () AS id, + divided.geom, + ST_Buffer(divided.geom::geography, {sub_region_buffer_dist})::geometry AS buffer_geom + FROM region, + LATERAL basic.divide_polygon(geom, {sub_region_grid_width}, {sub_region_grid_height}) AS divided; + """ + self.db.perform(sql_create_table) + + # Fetch list of sub-region IDs + sub_regions = self.db.select(f""" + SELECT id + FROM {self.local_sub_region_table}; + """) + self.sub_regions = [id[0] for id in sub_regions] + + + def clip_osm_data(self): + """Clip OSM data to the buffer geometry of each sub-region.""" + + # Initialise output directory + make_dir(self.output_dir) + + # Generate sub-region polygon filters + print_info(f"Generating OSM filters for region: {self.region}") for id in self.sub_regions: - id = int(id[0]) - print(f"Creating R5 region for region: {self.region}, sub-region: {id}") - region_name = f"region-{self.region}_{id}" - success = self.delete_region_r5(name=region_name) # Delete old region - if not success: - print(f"Unable to delete old R5 region: {region_name}") - break - region_id = self.create_region_r5( # Create new region with latest bounds for this sub-region - name=region_name, - description="", - bounds=self.get_sub_region_bounds(id=id) + osm_generate_polygon( + db_rd=self.db, + geom_query=f"SELECT buffer_geom as geom FROM {self.local_sub_region_table} WHERE id = {id}", + dest_file_path=os.path.join(self.output_dir, f"{id}.poly") ) - if region_id is None: - print(f"Unable to create new R5 region: {region_name}") - break - - print(f"Creating R5 network bundle for region: {self.region}, sub-region: {id}") - bundle_name = f"bundle-{self.region}_{id}" - success = self.delete_bundle_r5(name=bundle_name) # Delete old bundle - if not success: - print(f"Unable to delete old R5 bundle: {bundle_name}") - break - bundle_id = self.create_bundle_r5( # Create new bundle with latest OSM & GTFS data for this sub-region - name=bundle_name, - region_id=region_id, - osm_path=os.path.join(self.sub_region_osm_output_dir, f"{id}.pbf"), - gtfs_path=os.path.join(self.sub_region_gtfs_input_dir, f"{id}.zip") + + # Crop region OSM data as per sub-region polygon filters + for id in self.sub_regions: + print_info(f"Clipping OSM data for sub-region: {id}") + osm_crop_to_polygon( + orig_file_path=os.path.join(self.input_dir, self.config.preparation["local_osm_file"]), + dest_file_path=os.path.join(self.output_dir, f"{id}.o5m"), + poly_file_path=os.path.join(self.output_dir, f"{id}.poly") ) - if bundle_id is None: - print(f"Unable to create new R5 bundle: {bundle_name}") - break - - # Wait until previous bundle is processed - bundle_status = self.get_bundle_status_r5(id=bundle_id) - while bundle_status == "PROCESSING_OSM": - time.sleep(10) - bundle_status = self.get_bundle_status_r5(id=bundle_id) - if bundle_status != "DONE": - print(f"R5 engine failed to process bundle: {bundle_name}") - break - - - def get_sub_region_bounds(self, id): - bounds = self.db_rd.select( - f"""SELECT ST_XMin(buff_geom), ST_YMin(buff_geom), ST_XMax(buff_geom), ST_YMax(buff_geom) - FROM - ( - SELECT st_envelope(buffer_geom) AS buff_geom - FROM public.gtfs_regions - WHERE id = {id} - ) bg;""" - ) - return (bounds[0][0], bounds[0][1], bounds[0][2], bounds[0][3]) - - - def get_region_id_r5(self, name: str): - """Get the ID of a previously created region in R5""" - - region_id = None - response = requests.get(url=R5_FRONTEND_URL_REGIONS, headers=self.headers) - if response.status_code == 200: - for region in response.json(): - if region["name"] == name: - region_id = region["_id"] - break - return region_id - - - def delete_region_r5(self, name: str): - """Deletes a region from R5""" - - region_id = self.get_region_id_r5(name=name) - if region_id is None: - return True - response = requests.delete( - url=f"{R5_FRONTEND_URL_REGIONS}/{region_id}", - headers=self.headers - ) - return (response.status_code == 200) - - - def create_region_r5(self, name: str, description: str, bounds: tuple[float]): - """Creates a region in R5""" - - request_body = { - "name": name, - "description": description, - "bounds": { - "north": bounds[3], # ymax - "south": bounds[1], # ymin - "east": bounds[2], # xmax - "west": bounds[0], # xmin - } - } - response = requests.post( - url=R5_FRONTEND_URL_REGIONS, - json=request_body, - headers=self.headers - ) - region_id = None - if response.status_code == 201: - region_id = response.json()["_id"] - return region_id - - - def get_bundle_id_r5(self, name: str): - """Get the ID of a previously created bundle in R5""" - - bundle_id = None - response = requests.get(url=R5_BACKEND_URL_BUNDLE, headers=self.headers) - if response.status_code == 200: - for bundle in response.json(): - if bundle["name"] == name: - bundle_id = bundle["_id"] - break - return bundle_id - - - def get_bundle_status_r5(self, id: str): - """Get the status of a bundle in R5""" - - response = requests.get(url=f"{R5_BACKEND_URL_BUNDLE}/{id}", headers=self.headers) - return response.json()["status"] if response.status_code == 200 else None - - - def delete_bundle_r5(self, name: str): - """Deletes a bundle from R5""" - - bundle_id = self.get_bundle_id_r5(name=name) - if bundle_id is None: - return True - response = requests.delete( - url=f"{R5_BACKEND_URL_BUNDLE}/{bundle_id}", - headers=self.headers - ) - return (response.status_code == 200) - - - def create_bundle_r5(self, name: str, region_id: str, osm_path: str, gtfs_path: str): - """Creates a network bundle (GTFS + OSM data) in R5""" - - request_body = { - "bundleName": name, - "regionId": region_id - } - data_files = { - "osm": (os.path.basename(osm_path), open(osm_path, "rb")), - "feedGroup": (os.path.basename(gtfs_path), open(gtfs_path, "rb")) - } - response = requests.post( - url=R5_BACKEND_URL_BUNDLE, - data=request_body, - files=data_files, - headers=self.headers - ) - return response.json()["_id"] if response.status_code == 200 else None - + delete_file(file_path=os.path.join(self.output_dir, f"{id}.poly")) + + def process_osm_data(self): + """Further process and optimize OSM data.""" + # Filter OSM files to only include highways + print_info(f"Filtering clipped OSM datasets: {self.region}") + for id in self.sub_regions: + osm_filter_to_highways( + orig_file_path=os.path.join(self.output_dir, f"{id}.o5m"), + dest_file_path=os.path.join(self.output_dir, f"{id}_filtered.o5m"), + ) + delete_file(file_path=os.path.join(self.output_dir, f"{id}.o5m")) + + # Convert OSM files to PBF format + print_info(f"Processing filtered OSM datasets: {self.region}") + for id in self.sub_regions: + osm_convert( + orig_file_path=os.path.join(self.output_dir, f"{id}_filtered.o5m"), + dest_file_path=os.path.join(self.output_dir, f"{id}.pbf"), + ) + delete_file(file_path=os.path.join(self.output_dir, f"{id}_filtered.o5m")) + + def clip_gtfs_data(self): + """Clip GTFS data to the buffer geometry of each sub-region.""" + + # Generate sub-region GTFS datasets + for id in self.sub_regions: + print_info(f"Clipping GTFS data for sub-region: {id}") + + region_schema = self.config.preparation["local_gtfs_schema"] + sub_region_schema = f"network_pt_{self.region}_{id}" + + # Create new schema for sub-region data + self.db.perform(f""" + DROP SCHEMA IF EXISTS {sub_region_schema} CASCADE; + CREATE SCHEMA {sub_region_schema}; + """) + + # Create trips table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.trips AS + SELECT t.* + FROM {region_schema}.trips t, ( + SELECT DISTINCT(trip_id) AS trip_id + FROM ( + SELECT s.stop_id + FROM {region_schema}.stops s, + {self.local_sub_region_table} r + WHERE r.id = {id} + AND ST_Intersects(s.geom, r.buffer_geom) + ) s, + {region_schema}.stop_times st + WHERE st.stop_id = s.stop_id + ) sub + WHERE t.trip_id = sub.trip_id; + ALTER TABLE {sub_region_schema}.trips ADD PRIMARY KEY (trip_id); + CREATE INDEX ON {sub_region_schema}.trips (route_id); + """) + + # Create routes table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.routes AS ( + SELECT r.* + FROM {region_schema}.routes r, ( + SELECT DISTINCT(route_id) AS route_id + FROM {sub_region_schema}.trips t + ) sub + WHERE r.route_id = sub.route_id + ); + ALTER TABLE {sub_region_schema}.routes ADD PRIMARY KEY (route_id); + """) + + # Create agency table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.agency AS ( + SELECT a.* + FROM {region_schema}.agency a, ( + SELECT DISTINCT(agency_id) AS agency_id + FROM {sub_region_schema}.routes r + ) sub + WHERE a.agency_id = sub.agency_id + ); + ALTER TABLE {sub_region_schema}.agency ADD PRIMARY KEY (agency_id); + """) + + # Create calendar table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.calendar AS ( + SELECT c.* + FROM {region_schema}.calendar c, ( + SELECT DISTINCT(service_id) AS service_id + FROM {sub_region_schema}.trips t + ) sub + WHERE c.service_id = sub.service_id + ); + ALTER TABLE {sub_region_schema}.calendar ADD PRIMARY KEY (service_id); + CREATE INDEX ON {sub_region_schema}.calendar (start_date, end_date); + """) + + # Create calendar_dates table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.calendar_dates AS ( + SELECT cd.* + FROM {region_schema}.calendar_dates cd, ( + SELECT DISTINCT(service_id) AS service_id + FROM {sub_region_schema}.trips t + ) sub + WHERE cd.service_id = sub.service_id + ); + ALTER TABLE {sub_region_schema}.calendar_dates ADD PRIMARY KEY (service_id, date); + """) + + # Create shapes table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.shapes AS ( + SELECT s.* + FROM {region_schema}.shapes s, ( + SELECT DISTINCT(shape_id) AS shape_id + FROM {sub_region_schema}.trips t + ) sub + WHERE s.shape_id = sub.shape_id + ); + ALTER TABLE {sub_region_schema}.shapes ADD PRIMARY KEY (shape_id, shape_pt_sequence, h3_3); + """) + + def optimize_gtfs_data(self): + """Optimize the size of GTFS data by removing unnecessary trips.""" + + # Check if optimization is desired + if not self.config.preparation["weekday_tuesday"] or \ + not self.config.preparation["weekday_saturday"] or \ + not self.config.preparation["weekday_sunday"]: + return + + for id in self.sub_regions: + print_info(f"Optimizing GTFS data for sub-region: {id}") + + region_schema = self.config.preparation["local_gtfs_schema"] + sub_region_schema = f"network_pt_{self.region}_{id}" + + # Remove trips that are not scheduled to operate on the specified days + sql_delete_unused_trips = f""" + WITH unused_trips AS ( + WITH inactive_services AS ( + SELECT service_id + FROM {sub_region_schema}.calendar + WHERE tuesday = '0' + AND saturday = '0' + AND sunday = '0' + ), + active_dates AS ( + SELECT service_id + FROM {sub_region_schema}.calendar_dates + WHERE date IN ( + '{self.config.preparation["weekday_tuesday"]}'::DATE, + '{self.config.preparation["weekday_saturday"]}'::DATE, + '{self.config.preparation["weekday_sunday"]}'::DATE + ) + AND exception_type = 1 + ) + SELECT trip_id + FROM ( + SELECT * + FROM inactive_services + WHERE service_id NOT IN (SELECT * FROM active_dates) + ) inactive, + {sub_region_schema}.trips t + WHERE t.service_id = inactive.service_id + ) + DELETE FROM {sub_region_schema}.trips + WHERE trip_id IN (SELECT trip_id FROM unused_trips); + """ + self.db.perform(sql_delete_unused_trips) + + # Create stop_times table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.stop_times AS ( + SELECT st.* + FROM {region_schema}.stop_times st, + {sub_region_schema}.trips t + WHERE st.trip_id = t.trip_id + ); + ALTER TABLE {sub_region_schema}.stop_times ADD PRIMARY KEY (trip_id, stop_sequence, h3_3); + CREATE INDEX ON {sub_region_schema}.stop_times (stop_id, arrival_time, departure_time); + """) + + # Create stops table + self.db.perform(f""" + CREATE TABLE {sub_region_schema}.stops AS ( + SELECT s.* + FROM {region_schema}.stops s, + (SELECT DISTINCT stop_id, h3_3 FROM {sub_region_schema}.stop_times) st + WHERE s.h3_3 = st.h3_3 + AND s.stop_id = st.stop_id + ); + ALTER TABLE {sub_region_schema}.stops ADD PRIMARY KEY (stop_id, h3_3); + """) + + # Import missing parent stations and their child stops + self.db.perform(f""" + INSERT INTO {sub_region_schema}.stops + SELECT s.* + FROM {region_schema}.stops s, ( + SELECT DISTINCT(parent_station) AS parent_station + FROM {sub_region_schema}.stops + ) sub + WHERE s.stop_id = sub.parent_station + OR s.parent_station = sub.parent_station + ON CONFLICT DO NOTHING; + """) + +@timing def prepare_network_pt(region: str): - """Main function""" - + print_info(f"Preparing PT network for region: {region}") + db = Database(settings.LOCAL_DATABASE_URI) db_rd = Database(settings.RAW_DATABASE_URI) + try: - config = Config(name="network_pt", region=region) network_pt_preparation = NetworkPTPreparation( + db=db, db_rd=db_rd, - config=config.config, region=region ) - network_pt_preparation.upload_processed_data() + network_pt_preparation.create_sub_regions() + network_pt_preparation.clip_osm_data() + network_pt_preparation.process_osm_data() + network_pt_preparation.clip_gtfs_data() + network_pt_preparation.optimize_gtfs_data() + print_info(f"Finished preparing PT network for region: {region}") except Exception as e: - print(e) + print_error(f"Failed to prepare PT network for region: {region}") raise e finally: - db_rd.conn.close() + db.close() + db_rd.close() diff --git a/src/preparation/overture_place.py b/src/preparation/overture_place.py new file mode 100644 index 0000000..4b2744c --- /dev/null +++ b/src/preparation/overture_place.py @@ -0,0 +1,185 @@ +import csv + +from src.config.config import Config +from src.core.config import settings +from src.db.db import Database +from src.utils.utils import ( + print_error, + print_info, +) + + +class OverturePlacePreparation: + + def __init__(self, db: Database, db_rd: Database, region: str): + self.db = db + self.db_rd = db_rd + self.region = region + self.config = Config("overture_place", region) + + + def initialize_result_table(self): + """Create table for storing prepared data.""" + + sql_create_result_table = f""" + DROP TABLE IF EXISTS {self.config.preparation["local_result_table"]} CASCADE; + CREATE TABLE {self.config.preparation["local_result_table"]} ( + id serial NOT NULL UNIQUE, + overture_id text NOT NULL UNIQUE, + name text, + category text, + overture_category text, + overture_category_alternate jsonb, + confidence numeric, + source text, + website text, + social text, + email text, + phone text, + brand text, + address_freeform text, + address_locality text, + address_postcode text, + address_region text, + address_country text, + geom public.geometry(point, 4326) NOT NULL + ); + CREATE INDEX ON {self.config.preparation["local_result_table"]} USING gist (geom); + """ + self.db.perform(sql_create_result_table) + + + def parse_category_string(self, category_str): + """Cleans and splits category strings like '[retail,shopping,supermarket]' into a list.""" + + return category_str.strip("[]").split(",") + + def build_category_tree(self, category_list): + """Builds a nested dictionary from category paths.""" + + tree = {} + for category_str in category_list: + path = self.parse_category_string(category_str) + node = tree + for category in path: + if category not in node: + node[category] = {} + node = node[category] + + return tree + + def find_deepest_keys(self, d, result): + """Recursively finds all the deepest keys in a nested dictionary.""" + + for k, v in d.items(): + result.append(k) + if v: + self.find_deepest_keys(v, result) + + return result # Returns a set of deepest keys + + def process_place_data(self): + """Process Overture place data.""" + + # Load category mapping from CSV + csv_path = "/app/src/config/overture_categories.csv" + category_mapping = {} + with open(csv_path, mode="r", encoding="utf-8-sig") as csv_file: + csv_reader = csv.DictReader(csv_file, delimiter=";", skipinitialspace=True) + for row in csv_reader: + overture_taxonomy = row["Overture Taxonomy"] + category_code = row["Category code"] + category_mapping[overture_taxonomy] = category_code + + # Build category tree + category_tree = self.build_category_tree(category_mapping.keys()) + + # Process data for each category defined in the config + custom_categories = self.config.preparation["categories"] + for custom_category in custom_categories: + print_info(f"Processing category: {custom_category}") + + # Clip category tree to starting depth as defined in the config + filtered_tree = category_tree.copy() + starting_depth_key = None + for filter in custom_categories[custom_category]: + filtered_tree = filtered_tree[filter] + starting_depth_key = filter + + # Produce key to top-level category mapping + valid_sub_categories = {} + if not filtered_tree: + valid_sub_categories[starting_depth_key] = starting_depth_key + else: + valid_sub_categories[starting_depth_key] = "other" + + for new_cat in filtered_tree.keys(): + valid_sub_categories[new_cat] = new_cat + if filtered_tree[new_cat]: + for deep_cat in self.find_deepest_keys(filtered_tree[new_cat], []): + valid_sub_categories[deep_cat] = new_cat + + # Insert all POIs of each valid sub-category into the result table + total_count = len(valid_sub_categories) + index = 1 + for sub_category, top_category in valid_sub_categories.items(): + print_info(f"Processing sub-category: {sub_category}, {index} of {total_count}") + + sql_insert = f""" + WITH region AS ( + {self.config.preparation["region"]} + ) + INSERT INTO {self.config.preparation["local_result_table"]} ( + overture_id, name, category, overture_category, overture_category_alternate, + confidence, source, website, social, + email, phone, brand, address_freeform, + address_locality, address_postcode, + address_region, address_country, geom + ) + SELECT id, + (names::JSONB)->>'primary', %s, %s, (categories::JSONB)->'alternate', + confidence, 'Overture Maps Foundation', + websites[1], socials[1], emails[1], phones[1], + ((brand::JSONB)->'names')->>'primary', + (addresses::JSONB)[0]->>'freeform', + (addresses::JSONB)[0]->>'locality', + (addresses::JSONB)[0]->>'postcode', + (addresses::JSONB)[0]->>'region', + (addresses::JSONB)[0]->>'country', + geometry + FROM {self.config.preparation["local_source_table"]}, + region + WHERE (categories::JSONB)->>'primary' = %s + AND ST_Intersects(geometry, geom); + """ + self.db.perform(sql_insert, (top_category, sub_category, sub_category,)) + + index += 1 + + def run(self): + """Run Overture place preparation.""" + + self.initialize_result_table() + self.process_place_data() + + +def prepare_overture_place(region: str): + print_info(f"Prepare Overture place data for region: {region}.") + db = Database(settings.LOCAL_DATABASE_URI) + db_rd = Database(settings.RAW_DATABASE_URI) + + try: + OverturePlacePreparation( + db=db, + db_rd=db_rd, + region=region + ).run() + db.close() + db_rd.close() + print_info("Finished Overture network preparation.") + except Exception as e: + print_error(e) + raise e + finally: + db.close() + db_rd.close() diff --git a/src/preparation/network_overture.py b/src/preparation/overture_street_network.py similarity index 61% rename from src/preparation/network_overture.py rename to src/preparation/overture_street_network.py index daa43b2..6fc34db 100644 --- a/src/preparation/network_overture.py +++ b/src/preparation/overture_street_network.py @@ -1,6 +1,5 @@ import json import os -import subprocess import time from concurrent.futures import ThreadPoolExecutor, as_completed from queue import Queue @@ -10,88 +9,25 @@ from src.config.config import Config from src.core.config import settings from src.db.db import Database -from src.preparation.network_overture_parallelism import ( +from src.preparation.overture_street_network_helper import ( ComputeImpedance, ProcessSegments, ) from src.utils.utils import ( - delete_dir, - download_link, - make_dir, print_error, print_info, ) -class OvertureNetworkPreparation: +class OvertureStreetNetworkPreparation: def __init__(self, db: Database, db_rd: Database, region: str): self.db = db self.db_rd = db_rd self.region = region - self.config = Config("network_overture", region) + self.config = Config("overture_street_network", region) - self.NUM_THREADS = os.cpu_count() - 2 - - - def initialize_dem_table(self): - """Initialize digital elevation model (DEM) raster table.""" - - sql_create_dem_table = """ - DROP TABLE IF EXISTS public.dem; - CREATE TABLE public.dem ( - rid serial4 NOT NULL, - rast public.raster NULL, - filename text NULL, - CONSTRAINT dem_pkey PRIMARY KEY (rid), - CONSTRAINT enforce_num_bands_rast CHECK ((ST_NumBands(rast) = 1)), - CONSTRAINT enforce_srid_rast CHECK ((ST_SRID(rast) = 4326)) - ); - CREATE INDEX dem_st_convexhull_idx ON public.dem USING gist (ST_ConvexHull(rast)); - """ - self.db.perform(sql_create_dem_table) - - - def import_dem_tiles(self): - """Import digital elevation model (DEM) used for calculating slopes.""" - - print_info("Importing DEM tiles.") - - # Create directory for storing DEM related files - dem_dir = os.path.join(settings.INPUT_DATA_DIR, "network_overture", "dem") - delete_dir(dem_dir) - make_dir(dem_dir) - - # Get list of source URLs to fetch DEM tiles - dem_source_list_file_path = os.path.join( - settings.INPUT_DATA_DIR, - "network_overture", - self.config.preparation["dem_source_list"] - ) - dem_source_list = [] - with open(dem_source_list_file_path, "r") as file: - for line in file: - dem_source_list.append(line.strip()) - - # Download & import relevant DEM tiles - for index in range(len(dem_source_list)): - download_link( - link=dem_source_list[index], - directory=dem_dir - ) - try: - subprocess.run( - f"raster2pgsql -s 4326 -M -a {os.path.join(dem_dir, os.path.basename(dem_source_list[index]))} -F -t auto public.dem | " \ - f"PGPASSWORD='{settings.POSTGRES_PASSWORD}' psql -h {settings.POSTGRES_HOST} -U {settings.POSTGRES_USER} -d {settings.POSTGRES_DB}", - stdout = subprocess.DEVNULL, - shell=True, - check=True, - ) - except subprocess.CalledProcessError as e: - print_error(e) - raise e - - print_info(f"Imported DEM tile: {index + 1} of {len(dem_source_list)}.") + self.NUM_THREADS = 16 def initialize_connectors_table(self): @@ -100,15 +36,14 @@ def initialize_connectors_table(self): sql_create_connectors_table = """ DROP TABLE IF EXISTS basic.connector CASCADE; CREATE TABLE basic.connector ( - index serial NOT NULL UNIQUE, - id text NOT NULL UNIQUE, - osm_id int8 NULL, + id serial NOT NULL UNIQUE, + overture_id text NULL UNIQUE, geom public.geometry(point, 4326) NOT NULL, h3_3 integer NOT NULL, h3_6 integer NOT NULL, - CONSTRAINT connector_pkey PRIMARY KEY (index, h3_3) + CONSTRAINT connector_pkey PRIMARY KEY (id, h3_3) ); - CREATE INDEX idx_connector_id on basic.connector (id); + CREATE INDEX idx_connector_overture_id on basic.connector (overture_id); CREATE INDEX idx_connector_geom ON basic.connector USING gist (geom); """ self.db.perform(sql_create_connectors_table) @@ -121,12 +56,11 @@ def initialize_segments_table(self): DROP TABLE IF EXISTS basic.segment; CREATE TABLE basic.segment ( id serial NOT NULL, + overture_id text NOT NULL, length_m float8 NOT NULL, length_3857 float8 NOT NULL, - osm_id int8 NULL, - bicycle text NULL, - foot text NULL, class_ text NOT NULL, + subclass text NULL, impedance_slope float8 NULL, impedance_slope_reverse float8 NULL, impedance_surface float8 NULL, @@ -135,13 +69,12 @@ def initialize_segments_table(self): maxspeed_backward integer NULL, "source" integer NOT NULL, target integer NOT NULL, - tags jsonb NULL, geom public.geometry(linestring, 4326) NOT NULL, h3_3 integer NOT NULL, h3_6 integer NOT NULL, CONSTRAINT segment_pkey PRIMARY KEY (id, h3_3), - CONSTRAINT segment_source_fkey FOREIGN KEY ("source") REFERENCES basic.connector(index), - CONSTRAINT segment_target_fkey FOREIGN KEY (target) REFERENCES basic.connector(index) + CONSTRAINT segment_source_fkey FOREIGN KEY ("source") REFERENCES basic.connector(id), + CONSTRAINT segment_target_fkey FOREIGN KEY (target) REFERENCES basic.connector(id) ); CREATE INDEX idx_segment_geom ON basic.segment USING gist (geom); CREATE INDEX ix_basic_segment_source ON basic.segment USING btree (source); @@ -155,23 +88,15 @@ def compute_region_h3_grid(self): sql_get_region_geometry = f""" SELECT ST_AsText(geom) AS geom - FROM ({self.config.collection["region"]}) sub + FROM ({self.config.preparation["region"]}) sub """ region_geom = self.db_rd.select(sql_get_region_geometry)[0][0] - # TODO Remove this - with open("src/db/functions/fill_polygon_h3.sql", "r") as f: - self.db.perform(f.read()) - with open("src/db/functions/to_short_h3_3.sql", "r") as f: - self.db.perform(f.read()) - with open("src/db/functions/to_short_h3_6.sql", "r") as f: - self.db.perform(f.read()) - sql_create_region_h3_3_grid = f""" DROP TABLE IF EXISTS basic.h3_3_grid; CREATE TABLE basic.h3_3_grid AS SELECT * FROM - public.fill_polygon_h3(ST_GeomFromText('{region_geom}', 4326), 3); + basic.fill_polygon_h3(ST_GeomFromText('{region_geom}', 4326), 3); ALTER TABLE basic.h3_3_grid ADD CONSTRAINT h3_3_grid_pkey PRIMARY KEY (h3_index); CREATE INDEX ON basic.h3_3_grid USING GIST (h3_boundary); CREATE INDEX ON basic.h3_3_grid USING GIST (h3_geom); @@ -182,7 +107,7 @@ def compute_region_h3_grid(self): DROP TABLE IF EXISTS basic.h3_6_grid; CREATE TABLE basic.h3_6_grid AS SELECT * FROM - public.fill_polygon_h3(ST_GeomFromText('{region_geom}', 4326), 6); + basic.fill_polygon_h3(ST_GeomFromText('{region_geom}', 4326), 6); ALTER TABLE basic.h3_6_grid ADD CONSTRAINT h3_6_grid_pkey PRIMARY KEY (h3_index); CREATE INDEX ON basic.h3_6_grid USING GIST (h3_boundary); CREATE INDEX ON basic.h3_6_grid USING GIST (h3_geom); @@ -192,37 +117,47 @@ def compute_region_h3_grid(self): sql_compute_h3_short_index = """ ALTER TABLE basic.h3_3_grid ADD COLUMN h3_short integer; UPDATE basic.h3_3_grid - SET h3_short = to_short_h3_3(h3_index::bigint); + SET h3_short = basic.to_short_h3_3(h3_index::bigint); ALTER TABLE basic.h3_6_grid ADD COLUMN h3_short integer; UPDATE basic.h3_6_grid - SET h3_short = to_short_h3_6(h3_index::bigint); + SET h3_short = basic.to_short_h3_6(h3_index::bigint); """ self.db.perform(sql_compute_h3_short_index) print_info(f"Computed H3 grid for region: {self.region}.") + def open_database_connections(self, num_connections: int): + """Open multiple database connections for parallel processing.""" + + connections = [] + for _ in range(num_connections): + connection_string = f"dbname={settings.POSTGRES_DB} user={settings.POSTGRES_USER} \ + password={settings.POSTGRES_PASSWORD} host={settings.POSTGRES_HOST} \ + port={settings.POSTGRES_PORT}" + conn = psycopg2.connect(connection_string) + connections.append(conn) + return connections + + + def close_database_connections(self, connections: list): + """Close multiple database connections.""" + + [conn.close() for conn in connections] + + def initiate_segment_processing(self): """Utilize multithreading to process segments in parallel.""" - # TODO Remove this - with open("src/db/functions/classify_segment.sql", "r") as f: - self.db.perform(f.read()) - with open("src/db/functions/clip_segments.sql", "r") as f: - self.db.perform(f.read()) - # Load user-configured impedance coefficients for various surface types cycling_surfaces = json.dumps(self.config.preparation["cycling_surfaces"]) + # Load user-configured default speed limits for various road classes + default_speed_limits = json.dumps(self.config.preparation["default_speed_limits"]) + # Create separate DB connections for each thread - db_connections = [] - for _ in range(self.NUM_THREADS): - connection_string = f"dbname={settings.POSTGRES_DB} user={settings.POSTGRES_USER} \ - password={settings.POSTGRES_PASSWORD} host={settings.POSTGRES_HOST} \ - port={settings.POSTGRES_PORT}" - conn = psycopg2.connect(connection_string) - db_connections.append(conn) + db_connections = self.open_database_connections(self.NUM_THREADS) print_info(f"Starting {self.NUM_THREADS} threads for processing segments.") start_time = time.time() @@ -237,14 +172,39 @@ def initiate_segment_processing(self): ProcessSegments( thread_id=thread_id, db_connection=db_connections[thread_id], - get_next_h3_index=lambda: h3_3_queue.get() if not h3_3_queue.empty() else None, + get_next_h3_index=lambda: self.get_next_h3_3_index(h3_3_queue), cycling_surfaces=cycling_surfaces, + default_speed_limits=default_speed_limits, ).run ) for thread_id in range(self.NUM_THREADS) ] [future.result() for future in as_completed(futures)] + except Exception as e: + print_error(e) + raise e + finally: + # Clean up DB connections + self.close_database_connections(db_connections) + + # Create index on h3_6 column to support further processing + self.db.perform("CREATE INDEX ON basic.segment (h3_6);") + + print_info(f"Finished processing segments in {round((time.time() - start_time) / 60)} minutes.") + + + def compute_slope_impedance(self): + """Utilize multithreading to process segments in parallel.""" + + # Create separate DB connections for each thread + db_connections = self.open_database_connections(self.NUM_THREADS) + + print_info(f"Starting {self.NUM_THREADS} threads for computing slope impedance.") + start_time = time.time() + # Start threads + try: + with ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: # Compute segment impedance values h3_6_queue = self.get_h3_6_index_queue() futures = [ @@ -252,7 +212,7 @@ def initiate_segment_processing(self): ComputeImpedance( thread_id=thread_id, db_connection=db_connections[thread_id], - get_next_h3_index=lambda: h3_6_queue.get() if not h3_6_queue.empty() else None, + get_next_h3_index=lambda: self.get_next_h3_6_index(h3_6_queue), ).run ) for thread_id in range(self.NUM_THREADS) @@ -263,18 +223,27 @@ def initiate_segment_processing(self): raise e finally: # Clean up DB connections - [conn.close() for conn in db_connections] + self.close_database_connections(db_connections) + + # Fix remaining segments where impedance values could not be computed + print_info("Fixing remaining segments where impedance values could not be computed.") + sql_fix_remaining_segments = """ + UPDATE basic.segment + SET impedance_slope = 0, impedance_slope_reverse = 0 + WHERE impedance_slope IS NULL; + """ + self.db.perform(sql_fix_remaining_segments) - print_info(f"Finished processing segments in {round((time.time() - start_time) / 60)} minutes.") + print_info(f"Finished computing slope impedance in {round((time.time() - start_time) / 60)} minutes.") def get_h3_3_index_queue(self): """Get queue of H3 indexes to be processed by threads.""" sql_get_h3_indexes = """ - SELECT h3_index + SELECT h3_short FROM basic.h3_3_grid - ORDER BY h3_index; + ORDER BY h3_short; """ h3_indexes = self.db.select(sql_get_h3_indexes) h3_index_queue = Queue() @@ -283,13 +252,23 @@ def get_h3_3_index_queue(self): return h3_index_queue + def get_next_h3_3_index(self, h3_3_queue: Queue): + """Get next H3_3 cell index to be processed by a thread.""" + + if h3_3_queue.empty(): + return None + next_h3_3_index = h3_3_queue.get() + print_info(f"Processing H3_3 cell {next_h3_3_index}, remaining: {h3_3_queue.qsize()}") + return next_h3_3_index + + def get_h3_6_index_queue(self): """Get queue of H3 indexes to be processed by threads.""" sql_get_h3_indexes = """ SELECT h3_short FROM basic.h3_6_grid - ORDER BY h3_index; + ORDER BY h3_short; """ h3_indexes = self.db.select(sql_get_h3_indexes) h3_index_queue = Queue() @@ -298,39 +277,36 @@ def get_h3_6_index_queue(self): return h3_index_queue - def clean_up(self): - """Remove unused temp columns from the connector table.""" + def get_next_h3_6_index(self, h3_6_queue: Queue): + """Get next H3_6 cell index to be processed by a thread.""" - sql_clean_up = """ - ALTER TABLE basic.connector DROP COLUMN id; - ALTER TABLE basic.connector RENAME COLUMN index TO id; - """ - self.db.perform(sql_clean_up) + if h3_6_queue.empty(): + return None + next_h3_6_index = h3_6_queue.get() + if len(h3_6_queue.queue) % 1000 == 0: + print_info(f"Processing H3_6 cell {next_h3_6_index}, remaining: {h3_6_queue.qsize()}") + return next_h3_6_index def run(self): """Run Overture network preparation.""" - self.initialize_dem_table() - self.import_dem_tiles() - self.initialize_connectors_table() self.initialize_segments_table() self.compute_region_h3_grid() self.initiate_segment_processing() - - self.clean_up() + self.compute_slope_impedance() -def prepare_overture_network(region: str): +def prepare_overture_street_network(region: str): print_info(f"Prepare Overture network data for region: {region}.") db = Database(settings.LOCAL_DATABASE_URI) db_rd = Database(settings.RAW_DATABASE_URI) try: - OvertureNetworkPreparation( + OvertureStreetNetworkPreparation( db=db, db_rd=db_rd, region=region diff --git a/src/preparation/network_overture_parallelism.py b/src/preparation/overture_street_network_helper.py similarity index 72% rename from src/preparation/network_overture_parallelism.py rename to src/preparation/overture_street_network_helper.py index ede7c1f..27a3969 100644 --- a/src/preparation/network_overture_parallelism.py +++ b/src/preparation/overture_street_network_helper.py @@ -2,9 +2,9 @@ from threading import Thread import psycopg2 -from tqdm import tqdm -from src.utils.utils import print_error +from src.db.db import Database +from src.utils.utils import print_error, print_info class ProcessSegments(Thread): @@ -12,9 +12,10 @@ class ProcessSegments(Thread): def __init__( self, thread_id: int, - db_connection, + db_connection: Database, get_next_h3_index, - cycling_surfaces + cycling_surfaces: dict, + default_speed_limits: dict, ): super().__init__(group=None, target=self) @@ -23,37 +24,34 @@ def __init__( self.db_cursor = db_connection.cursor() self.get_next_h3_index = get_next_h3_index self.cycling_surfaces = cycling_surfaces + self.default_speed_limits = default_speed_limits def run(self): """Process segment data for this H3 index region""" - h3_index = self.get_next_h3_index() - while h3_index is not None: + h3_short = self.get_next_h3_index() + while h3_short is not None: # Get all segment IDs for this H3_3 index # Ensure segments are within valid H3_6 cells as well sql_get_segment_ids = f""" SELECT s.id FROM temporal.segments s, basic.h3_3_grid g1, basic.h3_6_grid g2 - WHERE - ST_Intersects(ST_Centroid(s.geometry), g1.h3_geom) + WHERE ST_Intersects(ST_Centroid(s.geometry), g1.h3_geom) AND ST_Intersects(ST_Centroid(s.geometry), g2.h3_geom) - AND g1.h3_index = '{h3_index}'; + AND g1.h3_short = '{h3_short}'; """ segment_ids = self.db_cursor.execute(sql_get_segment_ids) segment_ids = self.db_cursor.fetchall() # Process each segment - for index in tqdm( - range(len(segment_ids)), - desc=f"Thread {self.thread_id} - H3 index [{h3_index}]", - unit=" segments", mininterval=1, smoothing=0.0 - ): + for index in range(len(segment_ids)): id = segment_ids[index] sql_classify_segment = f""" - SELECT classify_segment( + SELECT basic.classify_segment( '{id[0]}', - '{self.cycling_surfaces}'::jsonb + '{self.cycling_surfaces}'::jsonb, + '{self.default_speed_limits}'::jsonb ); """ try: @@ -74,13 +72,13 @@ def run(self): except Exception as e: print_error(f"Thread {self.thread_id}: Failed to resolve deadlock, error: {e}.") else: - print_error(f"Thread {self.thread_id} failed to process H3 index {h3_index}, error: {e}.") + print_error(f"Thread {self.thread_id} failed to process H3 index {h3_short}, error: {e}.") break except Exception as e: - print_error(f"Thread {self.thread_id} failed to process H3 index {h3_index}, error: {e}.") + print_error(f"Thread {self.thread_id} failed to process H3 index {h3_short}, error: {e}.") break - h3_index = self.get_next_h3_index() + h3_short = self.get_next_h3_index() class ComputeImpedance(Thread): @@ -106,22 +104,22 @@ def run(self): while h3_short is not None: sql_update_impedance = f""" WITH segment AS ( - SELECT id, length_m, geom + SELECT id, length_m, geom, h3_6 FROM basic.segment WHERE h3_6 = {h3_short} + AND impedance_slope IS NULL ) UPDATE basic.segment AS sp - SET impedance_slope = c.imp, impedance_slope_reverse = c.rs_imp + SET impedance_slope = COALESCE(c.imp, 0), impedance_slope_reverse = COALESCE(c.rs_imp, 0) FROM segment, - LATERAL get_slope_profile(segment.geom, segment.length_m, ST_LENGTH(segment.geom)) s, - LATERAL compute_impedances(s.elevs, s.linklength, s.lengthinterval) c - WHERE sp.id = segment.id; + LATERAL basic.get_slope_profile(segment.geom, segment.length_m, ST_LENGTH(segment.geom)) s, + LATERAL basic.compute_impedances(s.elevs, s.linklength, s.lengthinterval) c + WHERE sp.h3_6 = segment.h3_6 + AND sp.id = segment.id; """ try: - start_time = time.time() self.db_cursor.execute(sql_update_impedance) self.db_connection.commit() - print(f"Thread {self.thread_id} updated impedance for H3 index {h3_short}. Time: {round(time.time() - start_time)} seconds.") except Exception as e: print_error(f"Thread {self.thread_id} failed to update impedances for H3 index {h3_short}, error: {e}.") break diff --git a/src/preparation/poi.py b/src/preparation/poi.py index 4e7d7bd..ec42a30 100755 --- a/src/preparation/poi.py +++ b/src/preparation/poi.py @@ -1,7 +1,8 @@ +import os + import geopandas as gpd import numpy as np import polars as pl -import os from src.config.config import Config from src.core.config import settings @@ -85,7 +86,7 @@ def read_poi(self) -> pl.DataFrame: f"""SELECT {column_names}, 'n' AS osm_type, ST_ASTEXT(way) AS geom FROM public.osm_poi_{self.region}_point""", f"""SELECT {column_names}, 'w' AS osm_type, ST_ASTEXT(ST_CENTROID(way)) AS geom FROM public.osm_poi_{self.region}_polygon""", ] - df = pl.read_database(sql_query, self.db_uri) + df = pl.read_database_uri(sql_query, self.db_uri) return df @timing @@ -708,7 +709,10 @@ def classify_poi(self, df): df = df.with_columns( pl.when( (pl.col("amenity") == "vending_machine") - & (pl.col("tags").apply(lambda tags: any(tag in tags for tag in ['food', 'bread', 'milk', 'eggs', 'meat', 'potato', 'honey', 'cheese']))) + & (pl.col("tags").map_elements( + lambda tags: any(tag in tags for tag in ['food', 'bread', 'milk', 'eggs', 'meat', 'potato', 'honey', 'cheese']), + return_dtype=pl.Boolean + )) ) .then(pl.lit("food_vending_machine")) .otherwise(pl.col("category")) diff --git a/src/utils/utils.py b/src/utils/utils.py index adbe5db..660ac5d 100755 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -686,18 +686,18 @@ def polars_df_to_postgis( # Close connection db.close() - - + + def osm_crop_to_polygon(orig_file_path: str, dest_file_path: str, poly_file_path: str): """ Crops OSM data as per polygon file Args: orig_file_path (str): Path to the input OSM data file - dest_file_path (str): Path to the output OSM data file (incl. filename with extension ".pbf") where OSM data is to be written + dest_file_path (str): Path to the output OSM data file (incl. filename with extension) where OSM data is to be written poly_file_path (str): Path to a polygon filter file (as per the format described here: https://wiki.openstreetmap.org/wiki/Osmosis/Polygon_Filter_File_Format) """ - + subprocess.run( f"osmconvert {orig_file_path} -B={poly_file_path} --complete-ways --drop-relations --drop-author --drop-version -o={dest_file_path}", shell=True, @@ -705,6 +705,38 @@ def osm_crop_to_polygon(orig_file_path: str, dest_file_path: str, poly_file_path ) +def osm_filter_to_highways(orig_file_path: str, dest_file_path: str): + """ + Crops OSM data as per polygon file + + Args: + orig_file_path (str): Path to the input OSM data file + dest_file_path (str): Path to the output OSM data file (incl. filename with extension) where OSM data is to be written + """ + + subprocess.run( + f'osmfilter {orig_file_path} --keep="highway=" -o={dest_file_path}', + shell=True, + check=True, + ) + + +def osm_convert(orig_file_path: str, dest_file_path: str): + """ + Crops OSM data as per polygon file + + Args: + orig_file_path (str): Path to the input OSM data file + dest_file_path (str): Path to the output OSM data file (incl. filename with extension) where OSM data is to be written + """ + + subprocess.run( + f'osmconvert {orig_file_path} -o={dest_file_path}', + shell=True, + check=True, + ) + + def osm_generate_polygon(db_rd, geom_query: str, dest_file_path: str): """ Generates a polygon filter file for cropping OSM data