2
# jVectorMap version 2.0.4
4
# Copyright 2011-2013, Kirill Lebedev
10
import shapely.affinity
18
def __init__(self, name, language):
21
self.language = language
26
def addPath(self, path, code, name):
27
self.paths[code] = {"path": path, "name": name}
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)+');'
35
def __init__(self, config):
37
'buffer_distance': -0.4,
38
'simplify_tolerance': 0.2,
49
self.map = Map(args['name'], args.get('language'))
51
if args.get('sources'):
52
self.sources = args['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')
66
'input_file_encoding': 'iso-8859-1'
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]
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
87
if args.get('viewport'):
88
self.viewport = map(lambda s: float(s), args.get('viewport').split(' '))
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)
100
if args.get('insets'):
101
self.insets = args.get('insets')
106
for sourceConfig in self.sources:
107
self.loadDataSource( sourceConfig )
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
115
transformation = osr.CoordinateTransformation( layer.GetSpatialRef(), self.spatialRef )
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])
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])
137
for feature in layer:
138
geometry = feature.GetGeometryRef()
139
geometryType = geometry.GetGeometryType()
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)
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)
152
if not shapelyGeometry.is_valid:
153
shapelyGeometry = shapelyGeometry.buffer(0, 1)
154
shapelyGeometry = self.applyFilters(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'))
159
code = '_' + str(nextCode)
162
self.features[code] = {"geometry": shapelyGeometry, "name": name, "code": code}
164
raise Exception, "Wrong geometry type: "+geometryType
167
def convert(self, outputFile):
168
print 'Generating '+outputFile
172
codes = self.features.keys()
173
main_codes = copy.copy(codes)
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'],
183
"width": inset['width'],
184
"height": insetHeight
187
shapely.geometry.box(
188
inset['left'], inset['top'], inset['left'] + inset['width'], inset['top'] + insetHeight
191
for code in inset['codes']:
192
main_codes.remove(code)
194
insetBbox = self.renderMapInset(main_codes, 0, 0, self.width)
195
insetHeight = (insetBbox[3] - insetBbox[1]) * (self.width / (insetBbox[2] - insetBbox[0]))
197
envelope.append( shapely.geometry.box( 0, 0, self.width, insetHeight ) )
198
mapBbox = shapely.geometry.MultiPolygon( envelope ).bounds
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]}],
207
"height": insetHeight
209
self.map.projection = {"type": self.projection, "centralMeridian": float(self.longitude0)}
211
open(outputFile, 'w').write( self.map.getJSCode() )
213
if self.for_each is not None:
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'])
221
def renderMapInset(self, codes, left, top, width):
224
envelope.append( self.features[code]['geometry'].envelope )
226
bbox = shapely.geometry.MultiPolygon( envelope ).bounds
228
scale = (bbox[2]-bbox[0]) / width
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:
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
243
polygons = [geometry]
245
for polygon in polygons:
247
rings.append(polygon.exterior)
248
rings.extend(polygon.interiors)
250
for pointIndex in range( len(ring.coords) ):
251
point = ring.coords[pointIndex]
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) )
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) )
259
self.map.addPath(path, feature['code'], feature['name'])
263
def applyFilters(self, geometry):
264
if self.viewportRect:
265
geometry = self.filterByViewport(geometry)
268
if self.minimal_area:
269
geometry = self.filterByMinimalArea(geometry)
275
def filterByViewport(self, geometry):
277
return geometry.intersection(self.viewportRect)
278
except shapely.geos.TopologicalError:
282
def filterByMinimalArea(self, geometry):
283
if isinstance(geometry, shapely.geometry.multipolygon.MultiPolygon):
284
polygons = geometry.geoms
286
polygons = [geometry]
287
polygons = filter(lambda p: p.area > self.minimal_area, polygons)
288
return shapely.geometry.multipolygon.MultiPolygon(polygons)
293
paramsJson = open(sys.argv[1], 'r').read()
295
paramsJson = sys.stdin.read()
296
paramsJson = json.loads(paramsJson)
298
converter = Converter(paramsJson)
299
converter.convert(paramsJson['output_file'])