205 lines
5.8 KiB
Python
205 lines
5.8 KiB
Python
|
import argparse
|
||
|
import sys
|
||
|
import os
|
||
|
from osgeo import ogr
|
||
|
from osgeo import osr
|
||
|
import anyjson
|
||
|
import shapely.geometry
|
||
|
import shapely.ops
|
||
|
import codecs
|
||
|
import time
|
||
|
|
||
|
|
||
|
format = '%.8f %.8f'
|
||
|
tolerance = 0.01
|
||
|
infile = '/Users/kirilllebedev/Maps/50m-admin-0-countries/ne_50m_admin_0_countries.shp'
|
||
|
outfile = 'map.shp'
|
||
|
|
||
|
# Open the datasource to operate on.
|
||
|
in_ds = ogr.Open( infile, update = 0 )
|
||
|
in_layer = in_ds.GetLayer( 0 )
|
||
|
in_defn = in_layer.GetLayerDefn()
|
||
|
|
||
|
|
||
|
# Create output file with similar information.
|
||
|
shp_driver = ogr.GetDriverByName( 'ESRI Shapefile' )
|
||
|
if os.path.exists('map.shp'):
|
||
|
shp_driver.DeleteDataSource( outfile )
|
||
|
shp_ds = shp_driver.CreateDataSource( outfile )
|
||
|
shp_layer = shp_ds.CreateLayer( in_defn.GetName(),
|
||
|
geom_type = in_defn.GetGeomType(),
|
||
|
srs = in_layer.GetSpatialRef() )
|
||
|
|
||
|
in_field_count = in_defn.GetFieldCount()
|
||
|
for fld_index in range(in_field_count):
|
||
|
src_fd = in_defn.GetFieldDefn( fld_index )
|
||
|
fd = ogr.FieldDefn( src_fd.GetName(), src_fd.GetType() )
|
||
|
fd.SetWidth( src_fd.GetWidth() )
|
||
|
fd.SetPrecision( src_fd.GetPrecision() )
|
||
|
shp_layer.CreateField( fd )
|
||
|
|
||
|
|
||
|
# Load geometries
|
||
|
geometries = []
|
||
|
for feature in in_layer:
|
||
|
geometry = feature.GetGeometryRef()
|
||
|
geometryType = geometry.GetGeometryType()
|
||
|
|
||
|
if geometryType == ogr.wkbPolygon or geometryType == ogr.wkbMultiPolygon:
|
||
|
shapelyGeometry = shapely.wkb.loads( geometry.ExportToWkb() )
|
||
|
#if not shapelyGeometry.is_valid:
|
||
|
#buffer to fix selfcrosses
|
||
|
#shapelyGeometry = shapelyGeometry.buffer(0)
|
||
|
if shapelyGeometry:
|
||
|
geometries.append(shapelyGeometry)
|
||
|
in_layer.ResetReading()
|
||
|
|
||
|
start = int(round(time.time() * 1000))
|
||
|
# Simplification
|
||
|
points = []
|
||
|
connections = {}
|
||
|
counter = 0
|
||
|
for geom in geometries:
|
||
|
counter += 1
|
||
|
polygons = []
|
||
|
|
||
|
if isinstance(geom, shapely.geometry.Polygon):
|
||
|
polygons.append(geom)
|
||
|
else:
|
||
|
for polygon in geom:
|
||
|
polygons.append(polygon)
|
||
|
|
||
|
for polygon in polygons:
|
||
|
if polygon.area > 0:
|
||
|
lines = []
|
||
|
lines.append(polygon.exterior)
|
||
|
for line in polygon.interiors:
|
||
|
lines.append(line)
|
||
|
|
||
|
for line in lines:
|
||
|
for i in range(len(line.coords)-1):
|
||
|
indexFrom = i
|
||
|
indexTo = i+1
|
||
|
pointFrom = format % line.coords[indexFrom]
|
||
|
pointTo = format % line.coords[indexTo]
|
||
|
if pointFrom == pointTo:
|
||
|
continue
|
||
|
if not (pointFrom in connections):
|
||
|
connections[pointFrom] = {}
|
||
|
connections[pointFrom][pointTo] = 1
|
||
|
if not (pointTo in connections):
|
||
|
connections[pointTo] = {}
|
||
|
connections[pointTo][pointFrom] = 1
|
||
|
print int(round(time.time() * 1000)) - start
|
||
|
|
||
|
simplifiedLines = {}
|
||
|
pivotPoints = {}
|
||
|
def simplifyRing(ring):
|
||
|
coords = list(ring.coords)[0:-1]
|
||
|
simpleCoords = []
|
||
|
|
||
|
isPivot = False
|
||
|
pointIndex = 0
|
||
|
while not isPivot and pointIndex < len(coords):
|
||
|
pointStr = format % coords[pointIndex]
|
||
|
pointIndex += 1
|
||
|
isPivot = ((len(connections[pointStr]) > 2) or (pointStr in pivotPoints))
|
||
|
pointIndex = pointIndex - 1
|
||
|
|
||
|
if not isPivot:
|
||
|
simpleRing = shapely.geometry.LineString(coords).simplify(tolerance)
|
||
|
if len(simpleRing.coords) <= 2:
|
||
|
return None
|
||
|
else:
|
||
|
pivotPoints[format % coords[0]] = True
|
||
|
pivotPoints[format % coords[-1]] = True
|
||
|
simpleLineKey = format % coords[0]+':'+format % coords[1]+':'+format % coords[-1]
|
||
|
simplifiedLines[simpleLineKey] = simpleRing.coords
|
||
|
return simpleRing
|
||
|
else:
|
||
|
points = coords[pointIndex:len(coords)]
|
||
|
points.extend(coords[0:pointIndex+1])
|
||
|
iFrom = 0
|
||
|
for i in range(1, len(points)):
|
||
|
pointStr = format % points[i]
|
||
|
if ((len(connections[pointStr]) > 2) or (pointStr in pivotPoints)):
|
||
|
line = points[iFrom:i+1]
|
||
|
lineKey = format % line[-1]+':'+format % line[-2]+':'+format % line[0]
|
||
|
if lineKey in simplifiedLines:
|
||
|
simpleLine = simplifiedLines[lineKey]
|
||
|
simpleLine = list(reversed(simpleLine))
|
||
|
else:
|
||
|
simpleLine = shapely.geometry.LineString(line).simplify(tolerance).coords
|
||
|
lineKey = format % line[0]+':'+format % line[1]+':'+format % line[-1]
|
||
|
simplifiedLines[lineKey] = simpleLine
|
||
|
simpleCoords.extend( simpleLine[0:-1] )
|
||
|
iFrom = i
|
||
|
if len(simpleCoords) <= 2:
|
||
|
return None
|
||
|
else:
|
||
|
return shapely.geometry.LineString(simpleCoords)
|
||
|
|
||
|
|
||
|
def simplifyPolygon(polygon):
|
||
|
simpleExtRing = simplifyRing(polygon.exterior)
|
||
|
if simpleExtRing is None:
|
||
|
return None
|
||
|
simpleIntRings = []
|
||
|
for ring in polygon.interiors:
|
||
|
simpleIntRing = simplifyRing(ring)
|
||
|
if simpleIntRing is not None:
|
||
|
simpleIntRings.append(simpleIntRing)
|
||
|
return shapely.geometry.Polygon(simpleExtRing, simpleIntRings)
|
||
|
|
||
|
|
||
|
results = []
|
||
|
for geom in geometries:
|
||
|
polygons = []
|
||
|
simplePolygons = []
|
||
|
|
||
|
if isinstance(geom, shapely.geometry.Polygon):
|
||
|
polygons.append(geom)
|
||
|
else:
|
||
|
for polygon in geom:
|
||
|
polygons.append(polygon)
|
||
|
|
||
|
for polygon in polygons:
|
||
|
simplePolygon = simplifyPolygon(polygon)
|
||
|
if not (simplePolygon is None or simplePolygon._geom is None):
|
||
|
simplePolygons.append(simplePolygon)
|
||
|
|
||
|
if len(simplePolygons) > 0:
|
||
|
results.append(shapely.geometry.MultiPolygon(simplePolygons))
|
||
|
else:
|
||
|
results.append(None)
|
||
|
|
||
|
|
||
|
# Process all features in input layer.
|
||
|
in_feat = in_layer.GetNextFeature()
|
||
|
counter = 0
|
||
|
while in_feat is not None:
|
||
|
if results[counter] is not None:
|
||
|
out_feat = ogr.Feature( feature_def = shp_layer.GetLayerDefn() )
|
||
|
out_feat.SetFrom( in_feat )
|
||
|
out_feat.SetGeometryDirectly(
|
||
|
ogr.CreateGeometryFromWkb(
|
||
|
shapely.wkb.dumps(
|
||
|
results[counter]
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
shp_layer.CreateFeature( out_feat )
|
||
|
out_feat.Destroy()
|
||
|
else:
|
||
|
print 'geometry is too small: '+in_feat.GetField(16)
|
||
|
|
||
|
in_feat.Destroy()
|
||
|
in_feat = in_layer.GetNextFeature()
|
||
|
counter += 1
|
||
|
|
||
|
|
||
|
# Cleanup
|
||
|
shp_ds.Destroy()
|
||
|
in_ds.Destroy()
|
||
|
|
||
|
print int(round(time.time() * 1000)) - start
|