# @version 0.2.0 from geojson import Point, LineString, Polygon, MultiPolygon, Feature, FeatureCollection from geojson import dumps as geodumps from csv import reader as csvreader #import json from pprint import pprint # For pretty-printing JSON #import sys #import urllib.request, urllib.parse, urllib.error # Inputs csvFileName = 'all-pois-italy.csv' # Function to convert SPARQL query results to GeoJSON FeatureCollection def sparql2geojs(csv): rows = 0 header = False features = [] for row in csv: if not header: header = True continue #pprint(result) featureId = row[0] feature = next((f for f in features if f and f['id'] == featureId), None) # SPARQL endpoint returns one POI more that once, if it has more than one property if feature is not None: updateGeoJSONFeature(feature, row) else: feature = createGeoJSONFeature(row) features.append(feature) rows += 1 if rows % 1000 == 0: print(rows, ' lines processed') return FeatureCollection(features) # Supplementary function to convert one result to a GeoJSON feature def createGeoJSONFeature(row): # each result from the SPARQL query comes with just one property-value pair props = {row[1]:row[2]} id = row[0] # ID of the feature is its URI geom = parseWKT(row[3]) #pprint(geom.get('type')) if geom.get('type').upper() == "POINT": #pprint(Feature(geometry = Point(coords), id = id, properties = props)) return Feature(geometry = geom, id = id, properties = props) elif geom.get('type').upper() == "MULTIPOLYGON": #pprint(Feature(geometry = MultiPolygon(coords), id = id, properties = props)) return Feature(geometry = MultiPolygon(coords), id = id, properties = props) # Supplementary function to update one GeoJSON feature if it already exists def updateGeoJSONFeature(feature, result): # Case 1: property does not exist in features's properties if result[1] not in feature['properties'].keys(): feature['properties'][result[1]] = result[2] else: # Case 2: property already exists, hence it has more than 1 value pass # Supplementary function to parse WKT Literal. # It is further extended by specific functions for specific geometry types def parseWKT(wktLiteral): type = wktLiteral.split("(")[0] if type.strip() == "POINT": wktCoords = wktLiteral[len(type):].replace("(", "").replace(")", "") return Point(parseWKTPoint(wktCoords)) elif type.strip() == "LINESTRING": wktCoords = wktLiteral[len(type):] return LineString(parseWKTLineString(wktCoords)) elif type.strip() == "POLYGON": wktCoords = wktLiteral[len(type)+1:-1] return Polygon(parseWKTPolygon(wktCoords)) elif type.strip() == "MULTIPOLYGON": wktCoords = wktLiteral[len(type)+1:-1] return MultiPolygon(parseWKTMultiPolygon(wktCoords)) # # find polygons first ... # parts = wktLiteral[15:].replace(')))', '').split(')),') #[15:] will intentionally left two ( in the beginning # print(len(parts)) # coords = [] # for part in parts: # # ... then rings of the polygons ... # rings = part.split('),') # print(len(rings)) # polygon = [] # this must be converted into tuple later on, but tuples are immutable # for r in rings: # # ... and finally points of the rings # points = part[2:].split(',') # pprint(points) # ring = [] # for point in points: # if len(point) > 1: # pprint(point) # ring.append( (float(point.split(' ')[0]), float(point.split(' ')[1])) ) # polygon.append(ring) # coords.append( tuple(polygon) ) # TODO: other geometry types support else: print("Unsupported geometry type {}! Will produce empty point object without coordinates!".format(type)) return Point([]) # Supplementary function to parse WKT Point geometry to its GeoJSON equivalent. def parseWKTPoint(wktLiteral): lonLat = wktLiteral.split(" ") return (float(lonLat[0]), float(lonLat[1])) # Supplementary function to parse WKT LineString geometry to its GeoJSON equivalent. def parseWKTLineString(wktLiteral): lineString = [] points = wktLiteral.replace("(", "").replace(")", "").split(",") for point in points: lineString.append(parseWKTPoint(point.strip())) return lineString # Supplementary function to parse WKT Polygon geometry to its GeoJSON equivalent. def parseWKTPolygon(wktLiteral): polygon = [] rings = wktLiteral.split(",(") for ring in rings: polygon.append(parseWKTLineString(ring)) return polygon # Supplementary function to parse WKT MultiPolygon geometry to its GeoJSON equivalent. def parseWKTMultiPolygon(wktLiteral): multipolygon = [] polygons = wktLiteral.split(",((") for polygon in polygons: multipolygon.append(parseWKTPolygon(polygon)) return multipolygon # Executional part with open(csvFileName, encoding='utf-8') as csvfile: filereader = csvreader(csvfile) print('File read, parsing ...') geojs = sparql2geojs(filereader) #pprint(js) #pprint(geojs) print(geojs.is_valid) print(geojs.errors()) # uncomment following lines if you want to save the output into a file outFileName = csvFileName.split(".")[0] with open(outFileName + ".geojson", 'w') as out: out.write(geodumps(geojs))