simplifier.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import argparse
  2. import sys
  3. import os
  4. from osgeo import ogr
  5. from osgeo import osr
  6. import anyjson
  7. import shapely.geometry
  8. import shapely.ops
  9. import codecs
  10. import time
  11. format = '%.8f %.8f'
  12. tolerance = 0.01
  13. infile = '/Users/kirilllebedev/Maps/50m-admin-0-countries/ne_50m_admin_0_countries.shp'
  14. outfile = 'map.shp'
  15. # Open the datasource to operate on.
  16. in_ds = ogr.Open( infile, update = 0 )
  17. in_layer = in_ds.GetLayer( 0 )
  18. in_defn = in_layer.GetLayerDefn()
  19. # Create output file with similar information.
  20. shp_driver = ogr.GetDriverByName( 'ESRI Shapefile' )
  21. if os.path.exists('map.shp'):
  22. shp_driver.DeleteDataSource( outfile )
  23. shp_ds = shp_driver.CreateDataSource( outfile )
  24. shp_layer = shp_ds.CreateLayer( in_defn.GetName(),
  25. geom_type = in_defn.GetGeomType(),
  26. srs = in_layer.GetSpatialRef() )
  27. in_field_count = in_defn.GetFieldCount()
  28. for fld_index in range(in_field_count):
  29. src_fd = in_defn.GetFieldDefn( fld_index )
  30. fd = ogr.FieldDefn( src_fd.GetName(), src_fd.GetType() )
  31. fd.SetWidth( src_fd.GetWidth() )
  32. fd.SetPrecision( src_fd.GetPrecision() )
  33. shp_layer.CreateField( fd )
  34. # Load geometries
  35. geometries = []
  36. for feature in in_layer:
  37. geometry = feature.GetGeometryRef()
  38. geometryType = geometry.GetGeometryType()
  39. if geometryType == ogr.wkbPolygon or geometryType == ogr.wkbMultiPolygon:
  40. shapelyGeometry = shapely.wkb.loads( geometry.ExportToWkb() )
  41. #if not shapelyGeometry.is_valid:
  42. #buffer to fix selfcrosses
  43. #shapelyGeometry = shapelyGeometry.buffer(0)
  44. if shapelyGeometry:
  45. geometries.append(shapelyGeometry)
  46. in_layer.ResetReading()
  47. start = int(round(time.time() * 1000))
  48. # Simplification
  49. points = []
  50. connections = {}
  51. counter = 0
  52. for geom in geometries:
  53. counter += 1
  54. polygons = []
  55. if isinstance(geom, shapely.geometry.Polygon):
  56. polygons.append(geom)
  57. else:
  58. for polygon in geom:
  59. polygons.append(polygon)
  60. for polygon in polygons:
  61. if polygon.area > 0:
  62. lines = []
  63. lines.append(polygon.exterior)
  64. for line in polygon.interiors:
  65. lines.append(line)
  66. for line in lines:
  67. for i in range(len(line.coords)-1):
  68. indexFrom = i
  69. indexTo = i+1
  70. pointFrom = format % line.coords[indexFrom]
  71. pointTo = format % line.coords[indexTo]
  72. if pointFrom == pointTo:
  73. continue
  74. if not (pointFrom in connections):
  75. connections[pointFrom] = {}
  76. connections[pointFrom][pointTo] = 1
  77. if not (pointTo in connections):
  78. connections[pointTo] = {}
  79. connections[pointTo][pointFrom] = 1
  80. print int(round(time.time() * 1000)) - start
  81. simplifiedLines = {}
  82. pivotPoints = {}
  83. def simplifyRing(ring):
  84. coords = list(ring.coords)[0:-1]
  85. simpleCoords = []
  86. isPivot = False
  87. pointIndex = 0
  88. while not isPivot and pointIndex < len(coords):
  89. pointStr = format % coords[pointIndex]
  90. pointIndex += 1
  91. isPivot = ((len(connections[pointStr]) > 2) or (pointStr in pivotPoints))
  92. pointIndex = pointIndex - 1
  93. if not isPivot:
  94. simpleRing = shapely.geometry.LineString(coords).simplify(tolerance)
  95. if len(simpleRing.coords) <= 2:
  96. return None
  97. else:
  98. pivotPoints[format % coords[0]] = True
  99. pivotPoints[format % coords[-1]] = True
  100. simpleLineKey = format % coords[0]+':'+format % coords[1]+':'+format % coords[-1]
  101. simplifiedLines[simpleLineKey] = simpleRing.coords
  102. return simpleRing
  103. else:
  104. points = coords[pointIndex:len(coords)]
  105. points.extend(coords[0:pointIndex+1])
  106. iFrom = 0
  107. for i in range(1, len(points)):
  108. pointStr = format % points[i]
  109. if ((len(connections[pointStr]) > 2) or (pointStr in pivotPoints)):
  110. line = points[iFrom:i+1]
  111. lineKey = format % line[-1]+':'+format % line[-2]+':'+format % line[0]
  112. if lineKey in simplifiedLines:
  113. simpleLine = simplifiedLines[lineKey]
  114. simpleLine = list(reversed(simpleLine))
  115. else:
  116. simpleLine = shapely.geometry.LineString(line).simplify(tolerance).coords
  117. lineKey = format % line[0]+':'+format % line[1]+':'+format % line[-1]
  118. simplifiedLines[lineKey] = simpleLine
  119. simpleCoords.extend( simpleLine[0:-1] )
  120. iFrom = i
  121. if len(simpleCoords) <= 2:
  122. return None
  123. else:
  124. return shapely.geometry.LineString(simpleCoords)
  125. def simplifyPolygon(polygon):
  126. simpleExtRing = simplifyRing(polygon.exterior)
  127. if simpleExtRing is None:
  128. return None
  129. simpleIntRings = []
  130. for ring in polygon.interiors:
  131. simpleIntRing = simplifyRing(ring)
  132. if simpleIntRing is not None:
  133. simpleIntRings.append(simpleIntRing)
  134. return shapely.geometry.Polygon(simpleExtRing, simpleIntRings)
  135. results = []
  136. for geom in geometries:
  137. polygons = []
  138. simplePolygons = []
  139. if isinstance(geom, shapely.geometry.Polygon):
  140. polygons.append(geom)
  141. else:
  142. for polygon in geom:
  143. polygons.append(polygon)
  144. for polygon in polygons:
  145. simplePolygon = simplifyPolygon(polygon)
  146. if not (simplePolygon is None or simplePolygon._geom is None):
  147. simplePolygons.append(simplePolygon)
  148. if len(simplePolygons) > 0:
  149. results.append(shapely.geometry.MultiPolygon(simplePolygons))
  150. else:
  151. results.append(None)
  152. # Process all features in input layer.
  153. in_feat = in_layer.GetNextFeature()
  154. counter = 0
  155. while in_feat is not None:
  156. if results[counter] is not None:
  157. out_feat = ogr.Feature( feature_def = shp_layer.GetLayerDefn() )
  158. out_feat.SetFrom( in_feat )
  159. out_feat.SetGeometryDirectly(
  160. ogr.CreateGeometryFromWkb(
  161. shapely.wkb.dumps(
  162. results[counter]
  163. )
  164. )
  165. )
  166. shp_layer.CreateFeature( out_feat )
  167. out_feat.Destroy()
  168. else:
  169. print 'geometry is too small: '+in_feat.GetField(16)
  170. in_feat.Destroy()
  171. in_feat = in_layer.GetNextFeature()
  172. counter += 1
  173. # Cleanup
  174. shp_ds.Destroy()
  175. in_ds.Destroy()
  176. print int(round(time.time() * 1000)) - start