diff --git a/Data/Results/Automatic_result.md b/Data/Results/Automatic_result.md index 2327914..3a11387 100644 --- a/Data/Results/Automatic_result.md +++ b/Data/Results/Automatic_result.md @@ -1,6 +1,6 @@ # Quality criterias result between OSM and OMF datasets -The test were run on 14/08/2024 14:22, using the 2024-06-13-beta.1 release of OvertureMap data and the OSM data until 2024/06/07. +The test were run on 21/08/2024 23:51, using the 2024-06-13-beta.1 release of OvertureMap data and the OSM data until 2024/06/07. ## General results diff --git a/Data/Results/Automatic_result_paris.md b/Data/Results/Automatic_result_paris.md deleted file mode 100644 index eb4890e..0000000 --- a/Data/Results/Automatic_result_paris.md +++ /dev/null @@ -1,46 +0,0 @@ -# Quality criterias result between OSM and OMF datasets - -The test were run on 24/07/2024 14:28, using the 2024-06-13-beta.1 release of OvertureMap data and the OSM data until 2024/06/07. - -## General results - -| **Criterion** | **Area** | **OSM Value** | **OMF Value** | **Difference (abs)** | -|----------------------------------------------|------------|-----------------|-----------------|------------------------| -| **1. Number of nodes** | *Paris* | 124312 | 97329 | 26983 | -| **2. Number of edges** | *Paris* | 170612 | 142409 | 28203 | -| **3. Total length (km)** | *Paris* | 3694 | 3614 | 80 | -| **4. Number of connected components** | *Paris* | 1311 | 1185 | 126 | -| **5. Number of strong connected components** | *Paris* | 1860 | 1911 | 51 | -| **6. Number of isolated nodes** | *Paris* | 18 | 0 | 18 | -| **7. Overlap indicator (%)** | *Paris* | 97.97 | 95.96 | 2.01 | -| **8. Number of corresponding nodes** | *Paris* | 95623 | 95623 | 0 | -| **9. Percentage of corresponding nodes (%)** | *Paris* | 76.92 | 98.25 | 21.33 | - -## Specific results - -### Total kilometer of roads by class - -#### *Paris*: - -| class | OSM - Total length (km) | OSM - Number of entities | OMF - Total length (km) | OMF - Number of entities | -|---------------|---------------------------|----------------------------|---------------------------|----------------------------| -| alley | 23.75 | 808 | 23.31 | 655 | -| crosswalk | 0 | 0 | 22.5 | 4831 | -| cycleway | 171.31 | 5273 | 170.32 | 4906 | -| driveway | 17.2 | 698 | 16.46 | 564 | -| footway | 571.55 | 56086 | 1014.17 | 56361 | -| living_street | 44.79 | 1542 | 44.54 | 1477 | -| motorway | 0.03 | 2 | 0 | 0 | -| parking_aisle | 30.29 | 1114 | 27.77 | 955 | -| path | 9.75 | 315 | 9.57 | 307 | -| pedestrian | 100.02 | 3415 | 100.29 | 3158 | -| primary | 138.09 | 4009 | 133.54 | 3674 | -| residential | 545.5 | 17318 | 543.61 | 16903 | -| secondary | 107.56 | 3552 | 111.46 | 3518 | -| sidewalk | 1559.95 | 60571 | 1038.15 | 31755 | -| steps | 39.24 | 4905 | 24.39 | 3413 | -| tertiary | 101.97 | 3505 | 102.19 | 3364 | -| track | 0.72 | 7 | 0.72 | 5 | -| trunk | 9.34 | 69 | 6.87 | 33 | -| unclassified | 28.42 | 1023 | 27.69 | 967 | -| unknown | 193.83 | 6400 | 195.63 | 5563 | diff --git a/Documentation/user-doc.md b/Documentation/user-doc.md index 220a9b2..a1461c5 100644 --- a/Documentation/user-doc.md +++ b/Documentation/user-doc.md @@ -133,7 +133,7 @@ python.exe -m pip install --upgrade pip **Install dependencies** ```cmd -pip install pip-tools && pip-compile Requirements\requirements.txt && pip install -r Requirements\requirements.txt +pip install pip-tools && pip-compile Requirements\requirements.in && pip install -r Requirements\requirements.txt ``` Of course, you have to adapt the path and names if you have changed anything. diff --git a/Python/osm.py b/Python/osm.py index efa009e..be71804 100644 --- a/Python/osm.py +++ b/Python/osm.py @@ -101,6 +101,7 @@ def addMissingColumns(connection:psycopg2.extensions.connection, def createTableToAggregateEdges(connection:psycopg2.extensions.connection, edgeTable:str, area:str, + utmProj:int, schema:str = 'public'): """Create a table to join parallel edges. @@ -108,6 +109,7 @@ def createTableToAggregateEdges(connection:psycopg2.extensions.connection, connection (psycopg2.extensions.connection): Database connection token. edgeTable (str): Name of the edge table. area (str): Name of the area. + utmProj (int): UTM projection for the area. schema (str, optional): Name of the schema. Defaults to 'public'. """ sql = f""" @@ -167,8 +169,8 @@ def createTableToAggregateEdges(connection:psycopg2.extensions.connection, FROM {schema}.{edgeTable} AS e1 LEFT JOIN {schema}.{edgeTable} AS e2 ON e1.u = e2.v AND e1.v = e2.u AND e1.id != e2.id AND e1.highway = e2.highway - AND ST_Contains(ST_Buffer(ST_Transform(e1.geom, 6691), 0.5), ST_Transform(e2.geom, 6691)) - AND ST_Contains(ST_Buffer(ST_Transform(e2.geom, 6691), 0.5), ST_Transform(e1.geom, 6691)) + AND ST_Contains(ST_Buffer(ST_Transform(e1.geom, {utmProj}), 0.5), ST_Transform(e2.geom, {utmProj})) + AND ST_Contains(ST_Buffer(ST_Transform(e2.geom, {utmProj}), 0.5), ST_Transform(e1.geom, {utmProj})) ORDER BY e1.id; -- Add cost and reverse cost columns @@ -186,8 +188,8 @@ def createTableToAggregateEdges(connection:psycopg2.extensions.connection, UPDATE {schema}.{area} SET reverse_cost = ST_Length(geom1::geography) WHERE u1 = v2 AND v1 = u2 AND highway1 = highway2 AND id1 != id2 - AND ST_Contains(ST_Buffer(ST_Transform(geom1, 6691), 0.5), ST_Transform(geom2, 6691)) - AND ST_Contains(ST_Buffer(ST_Transform(geom2, 6691), 0.5), ST_Transform(geom1, 6691)); + AND ST_Contains(ST_Buffer(ST_Transform(geom1, {utmProj}), 0.5), ST_Transform(geom2, {utmProj})) + AND ST_Contains(ST_Buffer(ST_Transform(geom2, {utmProj}), 0.5), ST_Transform(geom1, {utmProj})); CREATE INDEX {area}_geom1_idx ON {schema}.{area} USING gist (geom1); @@ -203,14 +205,16 @@ def createTableToAggregateEdges(connection:psycopg2.extensions.connection, def getBidirectionalRoads(engine:sqlalchemy.engine.base.Engine, - area:str, - schema:str = 'public', - geomColumn:str = 'geom1') -> gpd.GeoDataFrame: + area:str, + utmProj:int, + schema:str = 'public', + geomColumn:str = 'geom1') -> gpd.GeoDataFrame: """Get only bidirectional roads from the parallel edge table. Args: engine (sqlalchemy.engine.base.Engine): Engine with the database connection. area (str): Name of the area. + utmProj (int): UTM projection for the area. schema (str, optional): Name of the schema. Defaults to 'public'. geomColumn (str, optional): Name of the geomColumn to use. Defaults to 'geom1'. @@ -221,8 +225,8 @@ def getBidirectionalRoads(engine:sqlalchemy.engine.base.Engine, sql_bi_roads = f""" SELECT * FROM {schema}.{area} WHERE u1 = v2 AND v1 = u2 AND highway1 = highway2 - AND ST_Contains(ST_Buffer(ST_Transform(geom1, 6691), 0.5), ST_Transform(geom2, 6691)) - AND ST_Contains(ST_Buffer(ST_Transform(geom2, 6691), 0.5), ST_Transform(geom1, 6691));""" + AND ST_Contains(ST_Buffer(ST_Transform(geom1, {utmProj}), 0.5), ST_Transform(geom2, {utmProj})) + AND ST_Contains(ST_Buffer(ST_Transform(geom2, {utmProj}), 0.5), ST_Transform(geom1, {utmProj}));""" bi = gpd.read_postgis(sql_bi_roads, engine, geom_col=geomColumn) @@ -230,14 +234,16 @@ def getBidirectionalRoads(engine:sqlalchemy.engine.base.Engine, def getUnidirectionalRoads(engine:sqlalchemy.engine.base.Engine, - area:str, - schema:str = 'public', - geomColumn:str = 'geom1') -> gpd.GeoDataFrame: + area:str, + utmProj:int, + schema:str = 'public', + geomColumn:str = 'geom1') -> gpd.GeoDataFrame: """Get only unidirectional roads from the parallel edge table. Args: engine (sqlalchemy.engine.base.Engine): Engine with the database connection. area (str): Name of the area. + utmProj (int): UTM projection for the area. schema (str, optional): Name of the schema. Defaults to 'public'. geomColumn (str, optional): Name of the geomColumn to use. Defaults to 'geom1'. @@ -250,8 +256,8 @@ def getUnidirectionalRoads(engine:sqlalchemy.engine.base.Engine, WHERE id1 not in ( SELECT id1 FROM {schema}.{area} WHERE u1 = v2 AND v1 = u2 AND highway1 = highway2 - AND ST_Contains(ST_Buffer(ST_Transform(geom1, 6691), 0.5), ST_Transform(geom2, 6691)) - AND ST_Contains(ST_Buffer(ST_Transform(geom2, 6691), 0.5), ST_Transform(geom1, 6691)));""" + AND ST_Contains(ST_Buffer(ST_Transform(geom1, {utmProj}), 0.5), ST_Transform(geom2, {utmProj})) + AND ST_Contains(ST_Buffer(ST_Transform(geom2, {utmProj}), 0.5), ST_Transform(geom1, {utmProj})));""" uni = gpd.read_postgis(sql_uni_road, engine, geom_col=geomColumn) @@ -429,6 +435,13 @@ def createGraph(connection:psycopg2.extensions.connection, end = time.time() log(f"Save edge to postgis: {end - start} seconds") + # Get utm proj for the area + utmProj = utils.getUTMProjFromArea(connection, area) + + end = time.time() + log(f"UTM Proj is {utmProj}") + log(f"Utm proj: {end - start} seconds") + # Add missing columns if needed to the edge table addMissingColumns(connection, edgeTable, schema=schema) diff --git a/Python/utils.py b/Python/utils.py index 08d0ef6..e163f3a 100644 --- a/Python/utils.py +++ b/Python/utils.py @@ -1,6 +1,7 @@ import psycopg2 import sqlalchemy import os +import utm def bboxCSVToBboxWKT(bboxCSV:str) -> str: """Transform a bounding box in CSV format to its equivalent in OGC WKT format. @@ -383,7 +384,8 @@ def insertBoundingBox(connection:psycopg2.extensions.connection, connection (psycopg2.extensions.connection): Database connection token. wktGeom (str): Geometry in OGC WKT format. aeraName (str): Name of the area. - tableName (str, optional): Name of the table to create. Defaults to 'bounding_box'. + tableName (str, optional): Name of the bounding box table. + Defaults to 'bounding_box'. Return: int: Id of the inserted row @@ -495,4 +497,44 @@ def downloadOMFTypeBbox(bbox: str, print(f"{dataType} data has been downloaded") - return path \ No newline at end of file + return path + + +def getUTMProjFromArea(connection:psycopg2.extensions.connection, + aeraName:str, + tableName:str = 'bounding_box') -> int: + """Get UTM projection for a specific point. + + Args: + connection (psycopg2.extensions.connection): Database connection token. + aeraName (str): Name of the area. + tableName (str, optional): Name of the bounding box table. + Defaults to 'bounding_box'. + + Returns: + int: UTM projection crs id + """ + # Get centroid of the area + sql = f""" + WITH area_centroid AS ( + SELECT public.ST_Centroid(geom) as center FROM public.{tableName} + WHERE name = '{aeraName.capitalize()}' + LIMIT 1 + ) + SELECT public.ST_X(ac.center) AS lon, public.ST_Y(ac.center) AS lat + FROM area_centroid AS ac; + """ + + # Execute query + cursor = executeSelectQuery(connection, sql) + + # Get lat and lon from row + (lat, lon) = cursor.fetchone() + + # Get zone number + zone = utm.from_latlon(latitude = lat, longitude = lon)[2] + + # Return formatted string + epsgCode = f"326{zone:02d}" if lat >= 0 else f"327{zone:02d}" + + return int(epsgCode) \ No newline at end of file