lavkach3

Форк
0
299 строк · 10.2 Кб
1
#
2
# jVectorMap version 2.0.4
3
#
4
# Copyright 2011-2013, Kirill Lebedev
5
#
6

7
import sys
8
import shapely.geometry
9
import shapely.wkb
10
import shapely.affinity
11
from osgeo import ogr
12
from osgeo import osr
13
import json
14
import codecs
15
import copy
16

17
class Map:
18
  def __init__(self, name, language):
19
    self.paths = {}
20
    self.name = name
21
    self.language = language
22
    self.width = 0
23
    self.height = 0
24
    self.bbox = []
25

26
  def addPath(self, path, code, name):
27
    self.paths[code] = {"path": path, "name": name}
28

29
  def getJSCode(self):
30
    map = {"paths": self.paths, "width": self.width, "height": self.height, "insets": self.insets, "projection": self.projection}
31
    return "jQuery.fn.vectorMap('addMap', '"+self.name+"_"+self.projection['type']+"_"+self.language+"',"+json.dumps(map)+');'
32

33

34
class Converter:
35
  def __init__(self, config):
36
    args = {
37
      'buffer_distance': -0.4,
38
      'simplify_tolerance': 0.2,
39
      'longitude0': 0,
40
      'projection': 'mill',
41
      'name': 'world',
42
      'width': 900,
43
      'language': 'en',
44
      'precision': 2,
45
      'insets': []
46
    }
47
    args.update(config)
48

49
    self.map = Map(args['name'], args.get('language'))
50

51
    if args.get('sources'):
52
      self.sources = args['sources']
53
    else:
54
      self.sources = [{
55
        'input_file': args.get('input_file'),
56
        'where': args.get('where'),
57
        'name_field': args.get('name_field'),
58
        'code_field': args.get('code_field'),
59
        'input_file_encoding': args.get('input_file_encoding')
60
      }]
61

62
    default_source = {
63
      'where': '',
64
      'name_field': 0,
65
      'code_field': 1,
66
      'input_file_encoding': 'iso-8859-1'
67
    }
68

69
    for index in range(len(self.sources)):
70
      for key in default_source:
71
        if self.sources[index].get(key) is None:
72
          self.sources[index][key] = default_source[key]
73

74
    self.features = {}
75
    self.width = args.get('width')
76
    self.minimal_area = args.get('minimal_area')
77
    self.longitude0 = float(args.get('longitude0'))
78
    self.projection = args.get('projection')
79
    self.precision = args.get('precision')
80
    self.buffer_distance = args.get('buffer_distance')
81
    self.simplify_tolerance = args.get('simplify_tolerance')
82
    self.for_each = args.get('for_each')
83
    self.emulate_longitude0 = args.get('emulate_longitude0')
84
    if args.get('emulate_longitude0') is None and (self.projection == 'merc' or self.projection =='mill') and self.longitude0 != 0:
85
      self.emulate_longitude0 = True
86

87
    if args.get('viewport'):
88
      self.viewport = map(lambda s: float(s), args.get('viewport').split(' '))
89
    else:
90
      self.viewport = False
91

92
    # spatial reference to convert to
93
    self.spatialRef = osr.SpatialReference()
94
    projString = '+proj='+str(self.projection)+' +a=6381372 +b=6381372 +lat_0=0'
95
    if not self.emulate_longitude0:
96
      projString += ' +lon_0='+str(self.longitude0)
97
    self.spatialRef.ImportFromProj4(projString)
98

99
    # handle map insets
100
    if args.get('insets'):
101
      self.insets = args.get('insets')
102
    else:
103
      self.insets = []
104

105
  def loadData(self):
106
    for sourceConfig in self.sources:
107
      self.loadDataSource( sourceConfig )
108

109
  def loadDataSource(self, sourceConfig):
110
    source = ogr.Open( sourceConfig['input_file'] )
111
    layer = source.GetLayer(0)
112
    layer.SetAttributeFilter( sourceConfig['where'].encode('ascii') )
113
    self.viewportRect = False
114

115
    transformation = osr.CoordinateTransformation( layer.GetSpatialRef(), self.spatialRef )
116
    if self.viewport:
117
      layer.SetSpatialFilterRect( *self.viewport )
118
      point1 = transformation.TransformPoint(self.viewport[0], self.viewport[1])
119
      point2 = transformation.TransformPoint(self.viewport[2], self.viewport[3])
120
      self.viewportRect = shapely.geometry.box(point1[0], point1[1], point2[0], point2[1])
121

122
    layer.ResetReading()
123

124
    codes = {}
125

126
    if self.emulate_longitude0:
127
      meridian = -180 + self.longitude0
128
      p1 = transformation.TransformPoint(-180, 89)
129
      p2 = transformation.TransformPoint(meridian, -89)
130
      left = shapely.geometry.box(p1[0], p1[1], p2[0], p2[1])
131
      p3 = transformation.TransformPoint(meridian, 89)
132
      p4 = transformation.TransformPoint(180, -89)
133
      right = shapely.geometry.box(p3[0], p3[1], p4[0], p4[1])
134

135
    # load features
136
    nextCode = 0
137
    for feature in layer:
138
      geometry = feature.GetGeometryRef()
139
      geometryType = geometry.GetGeometryType()
140

141
      if geometryType == ogr.wkbPolygon or geometryType == ogr.wkbMultiPolygon:
142
        geometry.TransformTo( self.spatialRef )
143
        shapelyGeometry = shapely.wkb.loads( geometry.ExportToWkb() )
144
        if not shapelyGeometry.is_valid:
145
          shapelyGeometry = shapelyGeometry.buffer(0, 1)
146

147
        if self.emulate_longitude0:
148
          leftPart = shapely.affinity.translate(shapelyGeometry.intersection(left), p4[0] - p3[0])
149
          rightPart = shapely.affinity.translate(shapelyGeometry.intersection(right), p1[0] - p2[0])
150
          shapelyGeometry = leftPart.buffer(0.1, 1).union(rightPart.buffer(0.1, 1)).buffer(-0.1, 1)
151

152
        if not shapelyGeometry.is_valid:
153
          shapelyGeometry = shapelyGeometry.buffer(0, 1)
154
        shapelyGeometry = self.applyFilters(shapelyGeometry)
155
        if shapelyGeometry:
156
          name = feature.GetFieldAsString(str(sourceConfig.get('name_field'))).decode(sourceConfig.get('input_file_encoding'))
157
          code = feature.GetFieldAsString(str(sourceConfig.get('code_field'))).decode(sourceConfig.get('input_file_encoding'))
158
          if code in codes:
159
            code = '_' + str(nextCode)
160
            nextCode += 1
161
          codes[code] = name
162
          self.features[code] = {"geometry": shapelyGeometry, "name": name, "code": code}
163
      else:
164
        raise Exception, "Wrong geometry type: "+geometryType
165

166

167
  def convert(self, outputFile):
168
    print 'Generating '+outputFile
169

170
    self.loadData()
171

172
    codes = self.features.keys()
173
    main_codes = copy.copy(codes)
174
    self.map.insets = []
175
    envelope = []
176
    for inset in self.insets:
177
      insetBbox = self.renderMapInset(inset['codes'], inset['left'], inset['top'], inset['width'])
178
      insetHeight = (insetBbox[3] - insetBbox[1]) * (inset['width'] / (insetBbox[2] - insetBbox[0]))
179
      self.map.insets.append({
180
        "bbox": [{"x": insetBbox[0], "y": -insetBbox[3]}, {"x": insetBbox[2], "y": -insetBbox[1]}],
181
        "left": inset['left'],
182
        "top": inset['top'],
183
        "width": inset['width'],
184
        "height": insetHeight
185
      })
186
      envelope.append(
187
        shapely.geometry.box(
188
          inset['left'], inset['top'], inset['left'] + inset['width'], inset['top'] + insetHeight
189
        )
190
      )
191
      for code in inset['codes']:
192
        main_codes.remove(code)
193

194
    insetBbox = self.renderMapInset(main_codes, 0, 0, self.width)
195
    insetHeight = (insetBbox[3] - insetBbox[1]) * (self.width / (insetBbox[2] - insetBbox[0]))
196

197
    envelope.append( shapely.geometry.box( 0, 0, self.width, insetHeight ) )
198
    mapBbox = shapely.geometry.MultiPolygon( envelope ).bounds
199

200
    self.map.width = mapBbox[2] - mapBbox[0]
201
    self.map.height = mapBbox[3] - mapBbox[1]
202
    self.map.insets.append({
203
      "bbox": [{"x": insetBbox[0], "y": -insetBbox[3]}, {"x": insetBbox[2], "y": -insetBbox[1]}],
204
      "left": 0,
205
      "top": 0,
206
      "width": self.width,
207
      "height": insetHeight
208
    })
209
    self.map.projection = {"type": self.projection, "centralMeridian": float(self.longitude0)}
210

211
    open(outputFile, 'w').write( self.map.getJSCode() )
212

213
    if self.for_each is not None:
214
      for code in codes:
215
        childConfig = copy.deepcopy(self.for_each)
216
        for param in ('input_file', 'output_file', 'where', 'name'):
217
          childConfig[param] = childConfig[param].replace('{{code}}', code.lower())
218
        converter = Converter(childConfig)
219
        converter.convert(childConfig['output_file'])
220

221
  def renderMapInset(self, codes, left, top, width):
222
    envelope = []
223
    for code in codes:
224
      envelope.append( self.features[code]['geometry'].envelope )
225

226
    bbox = shapely.geometry.MultiPolygon( envelope ).bounds
227

228
    scale = (bbox[2]-bbox[0]) / width
229

230
    # generate SVG paths
231
    for code in codes:
232
      feature = self.features[code]
233
      geometry = feature['geometry']
234
      if self.buffer_distance:
235
        geometry = geometry.buffer(self.buffer_distance*scale, 1)
236
      if geometry.is_empty:
237
        continue
238
      if self.simplify_tolerance:
239
        geometry = geometry.simplify(self.simplify_tolerance*scale, preserve_topology=True)
240
      if isinstance(geometry, shapely.geometry.multipolygon.MultiPolygon):
241
        polygons = geometry.geoms
242
      else:
243
        polygons = [geometry]
244
      path = ''
245
      for polygon in polygons:
246
        rings = []
247
        rings.append(polygon.exterior)
248
        rings.extend(polygon.interiors)
249
        for ring in rings:
250
          for pointIndex in range( len(ring.coords) ):
251
            point = ring.coords[pointIndex]
252
            if pointIndex == 0:
253
              path += 'M'+str( round( (point[0]-bbox[0]) / scale + left, self.precision) )
254
              path += ','+str( round( (bbox[3] - point[1]) / scale + top, self.precision) )
255
            else:
256
              path += 'l' + str( round(point[0]/scale - ring.coords[pointIndex-1][0]/scale, self.precision) )
257
              path += ',' + str( round(ring.coords[pointIndex-1][1]/scale - point[1]/scale, self.precision) )
258
          path += 'Z'
259
      self.map.addPath(path, feature['code'], feature['name'])
260
    return bbox
261

262

263
  def applyFilters(self, geometry):
264
    if self.viewportRect:
265
      geometry = self.filterByViewport(geometry)
266
      if not geometry:
267
        return False
268
    if self.minimal_area:
269
      geometry = self.filterByMinimalArea(geometry)
270
      if not geometry:
271
        return False
272
    return geometry
273

274

275
  def filterByViewport(self, geometry):
276
    try:
277
      return geometry.intersection(self.viewportRect)
278
    except shapely.geos.TopologicalError:
279
      return False
280

281

282
  def filterByMinimalArea(self, geometry):
283
    if isinstance(geometry, shapely.geometry.multipolygon.MultiPolygon):
284
      polygons = geometry.geoms
285
    else:
286
      polygons = [geometry]
287
    polygons = filter(lambda p: p.area > self.minimal_area, polygons)
288
    return shapely.geometry.multipolygon.MultiPolygon(polygons)
289

290

291
args = {}
292
if len(sys.argv) > 1:
293
  paramsJson = open(sys.argv[1], 'r').read()
294
else:
295
  paramsJson = sys.stdin.read()
296
paramsJson = json.loads(paramsJson)
297

298
converter = Converter(paramsJson)
299
converter.convert(paramsJson['output_file'])
300

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.