123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- 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
|