| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 | 
							- #
 
- # jVectorMap version 2.0.4
 
- #
 
- # Copyright 2011-2013, Kirill Lebedev
 
- #
 
- import sys
 
- import shapely.geometry
 
- import shapely.wkb
 
- import shapely.affinity
 
- from osgeo import ogr
 
- from osgeo import osr
 
- import json
 
- import codecs
 
- import copy
 
- class Map:
 
-   def __init__(self, name, language):
 
-     self.paths = {}
 
-     self.name = name
 
-     self.language = language
 
-     self.width = 0
 
-     self.height = 0
 
-     self.bbox = []
 
-   def addPath(self, path, code, name):
 
-     self.paths[code] = {"path": path, "name": name}
 
-   def getJSCode(self):
 
-     map = {"paths": self.paths, "width": self.width, "height": self.height, "insets": self.insets, "projection": self.projection}
 
-     return "jQuery.fn.vectorMap('addMap', '"+self.name+"_"+self.projection['type']+"_"+self.language+"',"+json.dumps(map)+');'
 
- class Converter:
 
-   def __init__(self, config):
 
-     args = {
 
-       'buffer_distance': -0.4,
 
-       'simplify_tolerance': 0.2,
 
-       'longitude0': 0,
 
-       'projection': 'mill',
 
-       'name': 'world',
 
-       'width': 900,
 
-       'language': 'en',
 
-       'precision': 2,
 
-       'insets': []
 
-     }
 
-     args.update(config)
 
-     self.map = Map(args['name'], args.get('language'))
 
-     if args.get('sources'):
 
-       self.sources = args['sources']
 
-     else:
 
-       self.sources = [{
 
-         'input_file': args.get('input_file'),
 
-         'where': args.get('where'),
 
-         'name_field': args.get('name_field'),
 
-         'code_field': args.get('code_field'),
 
-         'input_file_encoding': args.get('input_file_encoding')
 
-       }]
 
-     default_source = {
 
-       'where': '',
 
-       'name_field': 0,
 
-       'code_field': 1,
 
-       'input_file_encoding': 'iso-8859-1'
 
-     }
 
-     for index in range(len(self.sources)):
 
-       for key in default_source:
 
-         if self.sources[index].get(key) is None:
 
-           self.sources[index][key] = default_source[key]
 
-     self.features = {}
 
-     self.width = args.get('width')
 
-     self.minimal_area = args.get('minimal_area')
 
-     self.longitude0 = float(args.get('longitude0'))
 
-     self.projection = args.get('projection')
 
-     self.precision = args.get('precision')
 
-     self.buffer_distance = args.get('buffer_distance')
 
-     self.simplify_tolerance = args.get('simplify_tolerance')
 
-     self.for_each = args.get('for_each')
 
-     self.emulate_longitude0 = args.get('emulate_longitude0')
 
-     if args.get('emulate_longitude0') is None and (self.projection == 'merc' or self.projection =='mill') and self.longitude0 != 0:
 
-       self.emulate_longitude0 = True
 
-     if args.get('viewport'):
 
-       self.viewport = map(lambda s: float(s), args.get('viewport').split(' '))
 
-     else:
 
-       self.viewport = False
 
-     # spatial reference to convert to
 
-     self.spatialRef = osr.SpatialReference()
 
-     projString = '+proj='+str(self.projection)+' +a=6381372 +b=6381372 +lat_0=0'
 
-     if not self.emulate_longitude0:
 
-       projString += ' +lon_0='+str(self.longitude0)
 
-     self.spatialRef.ImportFromProj4(projString)
 
-     # handle map insets
 
-     if args.get('insets'):
 
-       self.insets = args.get('insets')
 
-     else:
 
-       self.insets = []
 
-   def loadData(self):
 
-     for sourceConfig in self.sources:
 
-       self.loadDataSource( sourceConfig )
 
-   def loadDataSource(self, sourceConfig):
 
-     source = ogr.Open( sourceConfig['input_file'] )
 
-     layer = source.GetLayer(0)
 
-     layer.SetAttributeFilter( sourceConfig['where'].encode('ascii') )
 
-     self.viewportRect = False
 
-     transformation = osr.CoordinateTransformation( layer.GetSpatialRef(), self.spatialRef )
 
-     if self.viewport:
 
-       layer.SetSpatialFilterRect( *self.viewport )
 
-       point1 = transformation.TransformPoint(self.viewport[0], self.viewport[1])
 
-       point2 = transformation.TransformPoint(self.viewport[2], self.viewport[3])
 
-       self.viewportRect = shapely.geometry.box(point1[0], point1[1], point2[0], point2[1])
 
-     layer.ResetReading()
 
-     codes = {}
 
-     if self.emulate_longitude0:
 
-       meridian = -180 + self.longitude0
 
-       p1 = transformation.TransformPoint(-180, 89)
 
-       p2 = transformation.TransformPoint(meridian, -89)
 
-       left = shapely.geometry.box(p1[0], p1[1], p2[0], p2[1])
 
-       p3 = transformation.TransformPoint(meridian, 89)
 
-       p4 = transformation.TransformPoint(180, -89)
 
-       right = shapely.geometry.box(p3[0], p3[1], p4[0], p4[1])
 
-     # load features
 
-     nextCode = 0
 
-     for feature in layer:
 
-       geometry = feature.GetGeometryRef()
 
-       geometryType = geometry.GetGeometryType()
 
-       if geometryType == ogr.wkbPolygon or geometryType == ogr.wkbMultiPolygon:
 
-         geometry.TransformTo( self.spatialRef )
 
-         shapelyGeometry = shapely.wkb.loads( geometry.ExportToWkb() )
 
-         if not shapelyGeometry.is_valid:
 
-           shapelyGeometry = shapelyGeometry.buffer(0, 1)
 
-         if self.emulate_longitude0:
 
-           leftPart = shapely.affinity.translate(shapelyGeometry.intersection(left), p4[0] - p3[0])
 
-           rightPart = shapely.affinity.translate(shapelyGeometry.intersection(right), p1[0] - p2[0])
 
-           shapelyGeometry = leftPart.buffer(0.1, 1).union(rightPart.buffer(0.1, 1)).buffer(-0.1, 1)
 
-         if not shapelyGeometry.is_valid:
 
-           shapelyGeometry = shapelyGeometry.buffer(0, 1)
 
-         shapelyGeometry = self.applyFilters(shapelyGeometry)
 
-         if shapelyGeometry:
 
-           name = feature.GetFieldAsString(str(sourceConfig.get('name_field'))).decode(sourceConfig.get('input_file_encoding'))
 
-           code = feature.GetFieldAsString(str(sourceConfig.get('code_field'))).decode(sourceConfig.get('input_file_encoding'))
 
-           if code in codes:
 
-             code = '_' + str(nextCode)
 
-             nextCode += 1
 
-           codes[code] = name
 
-           self.features[code] = {"geometry": shapelyGeometry, "name": name, "code": code}
 
-       else:
 
-         raise Exception, "Wrong geometry type: "+geometryType
 
-   def convert(self, outputFile):
 
-     print 'Generating '+outputFile
 
-     self.loadData()
 
-     codes = self.features.keys()
 
-     main_codes = copy.copy(codes)
 
-     self.map.insets = []
 
-     envelope = []
 
-     for inset in self.insets:
 
-       insetBbox = self.renderMapInset(inset['codes'], inset['left'], inset['top'], inset['width'])
 
-       insetHeight = (insetBbox[3] - insetBbox[1]) * (inset['width'] / (insetBbox[2] - insetBbox[0]))
 
-       self.map.insets.append({
 
-         "bbox": [{"x": insetBbox[0], "y": -insetBbox[3]}, {"x": insetBbox[2], "y": -insetBbox[1]}],
 
-         "left": inset['left'],
 
-         "top": inset['top'],
 
-         "width": inset['width'],
 
-         "height": insetHeight
 
-       })
 
-       envelope.append(
 
-         shapely.geometry.box(
 
-           inset['left'], inset['top'], inset['left'] + inset['width'], inset['top'] + insetHeight
 
-         )
 
-       )
 
-       for code in inset['codes']:
 
-         main_codes.remove(code)
 
-     insetBbox = self.renderMapInset(main_codes, 0, 0, self.width)
 
-     insetHeight = (insetBbox[3] - insetBbox[1]) * (self.width / (insetBbox[2] - insetBbox[0]))
 
-     envelope.append( shapely.geometry.box( 0, 0, self.width, insetHeight ) )
 
-     mapBbox = shapely.geometry.MultiPolygon( envelope ).bounds
 
-     self.map.width = mapBbox[2] - mapBbox[0]
 
-     self.map.height = mapBbox[3] - mapBbox[1]
 
-     self.map.insets.append({
 
-       "bbox": [{"x": insetBbox[0], "y": -insetBbox[3]}, {"x": insetBbox[2], "y": -insetBbox[1]}],
 
-       "left": 0,
 
-       "top": 0,
 
-       "width": self.width,
 
-       "height": insetHeight
 
-     })
 
-     self.map.projection = {"type": self.projection, "centralMeridian": float(self.longitude0)}
 
-     open(outputFile, 'w').write( self.map.getJSCode() )
 
-     if self.for_each is not None:
 
-       for code in codes:
 
-         childConfig = copy.deepcopy(self.for_each)
 
-         for param in ('input_file', 'output_file', 'where', 'name'):
 
-           childConfig[param] = childConfig[param].replace('{{code}}', code.lower())
 
-         converter = Converter(childConfig)
 
-         converter.convert(childConfig['output_file'])
 
-   def renderMapInset(self, codes, left, top, width):
 
-     envelope = []
 
-     for code in codes:
 
-       envelope.append( self.features[code]['geometry'].envelope )
 
-     bbox = shapely.geometry.MultiPolygon( envelope ).bounds
 
-     scale = (bbox[2]-bbox[0]) / width
 
-     # generate SVG paths
 
-     for code in codes:
 
-       feature = self.features[code]
 
-       geometry = feature['geometry']
 
-       if self.buffer_distance:
 
-         geometry = geometry.buffer(self.buffer_distance*scale, 1)
 
-       if geometry.is_empty:
 
-         continue
 
-       if self.simplify_tolerance:
 
-         geometry = geometry.simplify(self.simplify_tolerance*scale, preserve_topology=True)
 
-       if isinstance(geometry, shapely.geometry.multipolygon.MultiPolygon):
 
-         polygons = geometry.geoms
 
-       else:
 
-         polygons = [geometry]
 
-       path = ''
 
-       for polygon in polygons:
 
-         rings = []
 
-         rings.append(polygon.exterior)
 
-         rings.extend(polygon.interiors)
 
-         for ring in rings:
 
-           for pointIndex in range( len(ring.coords) ):
 
-             point = ring.coords[pointIndex]
 
-             if pointIndex == 0:
 
-               path += 'M'+str( round( (point[0]-bbox[0]) / scale + left, self.precision) )
 
-               path += ','+str( round( (bbox[3] - point[1]) / scale + top, self.precision) )
 
-             else:
 
-               path += 'l' + str( round(point[0]/scale - ring.coords[pointIndex-1][0]/scale, self.precision) )
 
-               path += ',' + str( round(ring.coords[pointIndex-1][1]/scale - point[1]/scale, self.precision) )
 
-           path += 'Z'
 
-       self.map.addPath(path, feature['code'], feature['name'])
 
-     return bbox
 
-   def applyFilters(self, geometry):
 
-     if self.viewportRect:
 
-       geometry = self.filterByViewport(geometry)
 
-       if not geometry:
 
-         return False
 
-     if self.minimal_area:
 
-       geometry = self.filterByMinimalArea(geometry)
 
-       if not geometry:
 
-         return False
 
-     return geometry
 
-   def filterByViewport(self, geometry):
 
-     try:
 
-       return geometry.intersection(self.viewportRect)
 
-     except shapely.geos.TopologicalError:
 
-       return False
 
-   def filterByMinimalArea(self, geometry):
 
-     if isinstance(geometry, shapely.geometry.multipolygon.MultiPolygon):
 
-       polygons = geometry.geoms
 
-     else:
 
-       polygons = [geometry]
 
-     polygons = filter(lambda p: p.area > self.minimal_area, polygons)
 
-     return shapely.geometry.multipolygon.MultiPolygon(polygons)
 
- args = {}
 
- if len(sys.argv) > 1:
 
-   paramsJson = open(sys.argv[1], 'r').read()
 
- else:
 
-   paramsJson = sys.stdin.read()
 
- paramsJson = json.loads(paramsJson)
 
- converter = Converter(paramsJson)
 
- converter.convert(paramsJson['output_file'])
 
 
  |