def __init__(self, mapdirobj = None, gpsimage = True, maptype = MapTypeNormal, mapnumber=0, bigendian = False, inifile = None, hasmultiplemaps=True): self.gpsimage = gpsimage self.maptype = maptype self._mapnumber = mapnumber if mapdirobj == None: self.mapdir = mapdir.MapDirectory() else: if not isinstance(mapdirobj, mapdir.MapDirectory): raise ValueError("mapdirobj should be a MapDirectory object") self.mapdir = mapdirobj self.name = "Map" self.mode = None # Open mode ['r','w','a'] self.debug=False self.has_zip = True self.has_marine = (maptype == MapTypeStreetRoute) self._inifile = inifile self.inmemory = False ## If true all processing will be done in memory if maptype == MapTypeStreetRoute: self.routingcfg = routing.RoutingConfig() else: self.routingcfg = None ## Set endian self.bigendian = bigendian # Groups self._poigroup=None self.groups=[] self._searchgroups = [] ## Searchable group indices # Tables if maptype == MapTypeStreetRoute: self._ziptables = (0,1,0,0) else: self._ziptables = (0,0) self._marinetables = (0,1,0,1) ## Bounding box and bounding rect self._bboxrec = None self._boundrec = Rec((-198.316818, -99.475197), (198.316818, 99.475197)) ## Resolution and reference point self._scale = self.defaultscale self._refpoint = N.array([0.0, 0.0]) ## Config file self._cfg = ConfigParserUpper() self._initcfg() ## Layer and POI config self._laycfg = LayerConfig() self._poiconfig = POILayerConfig() ## Database self._db = None ## Other parameters otherparameters = ( ## Name, default value, description ('startscale', 4500.0, 'Start scale'), ('laycolor', 0, 'Unknown'), ('copyrightholders', ['Unknown'], 'Copyright holders'), ) ## Create properties for these parameters for param in otherparameters: setattr(self, '_' + param[0], param[1]) getfunc = lambda self, val: self.set_parameter('_'+param[0]) setfunc = lambda self, val: self.set_parameter('_'+param[0], val) setattr(self.__class__, param[0], property(getfunc, setfunc, doc=param[2])) ## Update config self._updatecfg() ## hasmultiplemaps is True if mapdir can have multiple maps with prefixes before their files self.hasmultiplemaps = hasmultiplemaps
class Map(object): formatversion = '1.01' defaultscale = N.array([9e-6, 9e-6]) def __init__(self, mapdirobj = None, gpsimage = True, maptype = MapTypeNormal, mapnumber=0, bigendian = False, inifile = None, hasmultiplemaps=True): self.gpsimage = gpsimage self.maptype = maptype self._mapnumber = mapnumber if mapdirobj == None: self.mapdir = mapdir.MapDirectory() else: if not isinstance(mapdirobj, mapdir.MapDirectory): raise ValueError("mapdirobj should be a MapDirectory object") self.mapdir = mapdirobj self.name = "Map" self.mode = None # Open mode ['r','w','a'] self.debug=False self.has_zip = True self.has_marine = (maptype == MapTypeStreetRoute) self._inifile = inifile self.inmemory = False ## If true all processing will be done in memory if maptype == MapTypeStreetRoute: self.routingcfg = routing.RoutingConfig() else: self.routingcfg = None ## Set endian self.bigendian = bigendian # Groups self._poigroup=None self.groups=[] self._searchgroups = [] ## Searchable group indices # Tables if maptype == MapTypeStreetRoute: self._ziptables = (0,1,0,0) else: self._ziptables = (0,0) self._marinetables = (0,1,0,1) ## Bounding box and bounding rect self._bboxrec = None self._boundrec = Rec((-198.316818, -99.475197), (198.316818, 99.475197)) ## Resolution and reference point self._scale = self.defaultscale self._refpoint = N.array([0.0, 0.0]) ## Config file self._cfg = ConfigParserUpper() self._initcfg() ## Layer and POI config self._laycfg = LayerConfig() self._poiconfig = POILayerConfig() ## Database self._db = None ## Other parameters otherparameters = ( ## Name, default value, description ('startscale', 4500.0, 'Start scale'), ('laycolor', 0, 'Unknown'), ('copyrightholders', ['Unknown'], 'Copyright holders'), ) ## Create properties for these parameters for param in otherparameters: setattr(self, '_' + param[0], param[1]) getfunc = lambda self, val: self.set_parameter('_'+param[0]) setfunc = lambda self, val: self.set_parameter('_'+param[0], val) setattr(self.__class__, param[0], property(getfunc, setfunc, doc=param[2])) ## Update config self._updatecfg() ## hasmultiplemaps is True if mapdir can have multiple maps with prefixes before their files self.hasmultiplemaps = hasmultiplemaps def set_scale(self, scale): """Set discrete unit as a xscale, yscale sequence""" try: self._scale = N.array(list(scale)) except TypeError: self._scale = scale * N.ones((2)) def get_scale(self): return self._scale scale = property(get_scale, set_scale, doc="Discretization unit vector") def set_refpoint(self, refpoint): self._refpoint = N.array(refpoint) def get_refpoint(self): return self._refpoint refpoint = property(get_refpoint, set_refpoint, doc="Reference point for the discretization process") def set_bbox(self, points): self.bboxrec = Rec(tuple(points[0]), tuple(points[1])) def get_bbox(self): bboxrec = self.bboxrec return ((bboxrec.minX(), bboxrec.minY()), (bboxrec.maxX(), bboxrec.maxY())) def set_parameter(self, parameter, value): if self.mode in ['r', 'a']: raise ValueError("Parameter %s can only be set in 'w' mode"%parameter) self.__dict__[parameter] = value def get_parameter(self, parameter): return self.__dict__[parameter] def set_bboxrec(self, rec): if self.mode in ['r', 'a']: raise ValueError("Bounding box can only be set in 'w' mode") if len(self.layers) > 0: raise ValueError("Set bounding box before defining layers") if isinstance(rec, Rec): self._bboxrec = rec.negY().toFloat32() else: raise ValueError("rec must be a Rec object") def get_bboxrec(self): if self._bboxrec == None: return None return self._bboxrec.negY() bbox = property(get_bbox, set_bbox, doc='Bounding box ((xmin,ymin),(xmax,ymax))') bboxrec = property(get_bboxrec, set_bboxrec, doc='Bounding box rectangle') def open(self, mode='r'): if self.mode != None: return None self.mode = mode if mode == 'w': if self.gpsimage: if self._inifile == None: self._inifile = self.mapnumstr + 'map.ini' ## Create database self._db = Database(self.mapdir, 'db' + self.mapnumstr, mode, self.bigendian) ## Create zip table if self.has_zip: buildziprecord(self._db, zipfilename = self.mapnumstr + 'z.dat', auxfilename = self.mapnumstr + 'cn.dat', extended = self.maptype == MapTypeStreetRoute) ## Create marine table if self.has_marine: buildmarinerecord(self._db, filenameprefix = self.mapnumstr) ## Create basic groups if self.maptype == MapTypeStreetRoute: roads = GroupStreet(self, name=self.mapnumstr + "_Roads") else: roads = GroupNormal(self, name=self.mapnumstr + "_Roads") roads.searchable = True self.addGroup(roads) self.addGroup(GroupNormal(self, name=self.mapnumstr + "_Railroads")) self.addGroup(GroupNormal(self, name=self.mapnumstr + "_Hydrography")) self.addGroup(GroupNormal(self, name=self.mapnumstr + "_Parks")) else: if self._inifile == None: self._inifile = 'map.ini' elif mode in ('r','a'): ## Find ini file if self._inifile == None: filelist = self.mapdir.listdir() possibleinifiles = ('map.ini', self.mapnumstr + 'map.ini', 'lay_info.ini') found = False for filename in possibleinifiles: if filename in filelist: self._inifile = filename found = True break if not found: raise ValueError('Could not find ini-file') self._cfg.readfp(IniFileFilter(self.mapdir.open(self._inifile))) # Get map type if self._cfg.has_option('MAP_INFO', 'MAPTYPE'): self.maptype = self._cfg.get("MAP_INFO", "MAPTYPE") # Get bounding box if self._cfg.has_option('MAP_INFO', 'BND_BOX'): bbox = map(float, self._cfg.get("MAP_INFO", "BND_BOX").split(" ")) self._bboxrec = Rec([bbox[0],bbox[2]], [bbox[1], bbox[3]]) # Get map name self.name = self._cfg.get('MAP_INFO', 'MAP_NAME') # self.date = self._cfg.get('MAP_INFO', 'MAP_DATE') self._laycfg.setupfromcfg(self._cfg, self) # Find database path dbname = self._cfg.get("LAYERS","DB_NAME") dbname = re.sub('\\\\', os.sep, dbname) # Since Windows is case insensitive, try to match the path case insensitive plist=dbname.split(os.sep) for i in range(0,len(plist)-2): path=os.sep.join(plist[0:i+1]) if len(path)>0: hits=[d for d in self.mapdir.listdir(path) if d.lower() == plist[i+1].lower()] if len(hits)==0: raise ValueError("Couldnt find database") plist[i+1]=hits[0] dbname = os.sep.join(plist) if dbname: self._db = Database(self.mapdir, dbname, self.mode, self.bigendian) # Read groups if self.debug: print "Groups:" for i in range(0,self._cfg.getint("GROUPS","NUMBER")): thegroup = groupFactory(self, i, self._cfg, self._db) self.groups.append(thegroup) thegroup.initFromIni(self._cfg) # Read POI file and POI layers if self._cfg.has_section("POI"): poi_ini = self._cfg.get("POI","POI_CONFIG") self.poicfg = ConfigParserUpper() if self.mapdir.isfile(poi_ini): self.poicfg.readfp(IniFileFilter(self.mapdir.open(poi_ini))) if self.poicfg.has_section("LAYERS"): self._poigroup = POIGroup(self) self._poiconfig.setupfromcfg(self.poicfg, self) # Read routing info if self.maptype == MapTypeStreetRoute: self.routingcfg = routing.RoutingConfig() self.routingcfg.setupfromcfg(self._cfg, self) # Read unpack tables if self._cfg.has_section('PACK_LAYS'): i = 0 while self._cfg.has_option('PACK_LAYS', str(i)): fields = self._cfg.get('PACK_LAYS', str(i)).split(' ') filename = fields[0] n = int(fields[1]) layernumbers = map(int, fields[2:]) assert len(layernumbers) == n for layernumber in layernumbers: self.getLayerByIndex(layernumber).setUnpackTable(filename) i += 1 def calculatebbox(self): """Calculate total bounding box rectangle from layers""" def layerbboxunion(a, b): if a == None and b == None: return None elif a == None: return b elif b == None: return a else: return a.union(b) return reduce(layerbboxunion, [layer.bboxrec for layer in self.layers + self._poiconfig.layers], None) def close(self): if self.mode == None: return write = self.mode in ['a','w'] ## Optimize layers in groups logging.info('Optimizing cell structure of normal layers') remaininglayers = list(self.layers + self._poiconfig.layers) if self.groups != None: for group in self.groups + [self._poigroup]: group.optimizeLayers() for layer in group.layers: remaininglayers.remove(layer) ## Create routing layers and build routing network if self.maptype == MapTypeStreetRoute: if self.mode == 'w': logging.info('Building routing network') self.routingcfg.build_routing_network(self) elif self.mode == 'a': raise Exception('Updating of routing network not implemented') ## Copying routing data file self.mapdir.copyfile(os.path.join(datadir, 'routing.dat')) ## Optimize remaining layers if self.maptype == MapTypeStreetRoute: logging.info('Optimizing cell structure of routing layers') for layer in remaininglayers: layer.optimize() ## Update ini file if write: if self._bboxrec == None: bbox = self.calculatebbox() if bbox == None: raise ValueError("None of the layers contain a bounding box") self._bboxrec = bbox.negY() self._updatecfg() ## Copy bounding box from map for layers without one logging.info('Closing map') for layer in self.layers + self._poiconfig.layers: if layer.bboxrec == None: layer.bboxrec = self.bboxrec # Close groups and update INI file if self.groups != None: for group in self.groups: group.close() self.groups = None # Close poi group and poi layers if self._poigroup != None: self._poigroup.close() if write: if self._cfg.has_section("POI"): poiconfig = self._cfg.get("POI","POI_CONFIG") self.poicfg.write(self.mapdir.open(poiconfig, "wb")) self._poiconfig.close() # Close layers self._laycfg.close() # Write ini file if write: # Convert section keys to uppercase self._cfg.write(self.mapdir.open(self._inifile, "wb")) self.poicfg = None if self._db != None: self._db.close() del self._db self._db = None self.mode = None def getGroupByIndex(self, index): return self.groups[index] def getGroupByName(self, name): for group in self.groups: if group.name == name: return group def getGroupNames(self): return [group.name for group in self.groups] def getGroupIndex(self, group): if group in self.groups: return self.groups.index(group) elif group == self._poigroup: return 0 else: raise Exception('Group is not registered') def addGroup(self, group): if group.name in [g.name for g in self.groups]: raise ValueError("Group already added") if self._poigroup: raise ValueError('Normal groups cannot be added after POI groups') groupnum = len(self.groups) self.groups.append(group) group.open('w') def getLayerByIndex(self, index): return self._laycfg.getLayerByIndex(index) def getLayerStyle(self, layer): return self._laycfg.getLayerStyleByIndex(self.getLayerIndex(layer)) def getLayerIndex(self, layer): return self._laycfg.getLayerIndex(layer) def getLayerAndGroupByName(self, name): """Get layer by name and the group it belongs to""" for group in self.groups: for layer in group.layers: if layer.name == name: return layer, group layer = self._laycfg.getLayerByName(name) if layer != None: return layer, None else: raise ValueError("Layer %s not found"%name) @property def layers(self): return self._laycfg.layers def addLayer(self, layer, layerstyle = DetailMapLayerStyle()): if layer.filename in [l.filename for l in self._laycfg.layers]: raise Exception('Filename %s of layer %s is already used by another layer'%(l.filename, l.name)) self._laycfg.addLayer(layer, layerstyle = layerstyle) return layer def addPOIGroupAndLayer(self): if self.mode == 'r': raise ValueError("Can't add POI layer in read-only mode") if self._poigroup == None: # Create POI config file self.poicfg = ConfigParserUpper() self._cfg.set("POI","POI_CONFIG", self.mapnumstr + "poi.cfg") # Create POI group poigroup = POIGroup(self) poigroup.open('w') self._poigroup = poigroup # Create POI Layer layer=Layer(self, "poi", "poi", layertype=LayerTypePOI, fileidentifier=0xc0f0) layer.open('w') self._poiconfig.addLayer(layer, layerstyle=POILayerStyle()) def getDB(self): return self._db def getPOIGroup(self): return self._poigroup def getPOILayers(self): return self._poiconfig.layers def writeToMapDir(self, mapdir): mapdir.copyfrom(self.mapdir) def writeImage(self, filename): image = mapdir.Image(filename, mode='w', bigendian=self.bigendian) self.writeToMapDir(image) return image def addRoutingLayer(self, layer, routingsetnumber, direction = 'N', speed = (1, 1)): if self.maptype != MapTypeStreetRoute: raise ValueError('Map is not of type street route') self.routingcfg.addRoutingLayer(self, layer, routingsetnumber, direction = 'N', speed = (1, 1)) def _updatecfg(self): """Update cfg data""" ## Map info self._cfg.set('MAP_INFO', 'VERSION', str(self.formatversion)) if self.bigendian: byteorder = 'M' else: byteorder = 'I' self._cfg.set('MAP_INFO', 'BYTE_ORDER', byteorder) self._cfg.set('MAP_INFO', 'IS_ADD', str(0)) self._cfg.set('MAP_INFO', 'MAP_NAME', self.name) self._cfg.set('MAP_INFO', 'START_SCALE', str(self._startscale)) ## Color 4 bits self._cfg.set('COLORS4BITS', 'LAY_COLOR', str(self._laycolor)) ## Write groups self._cfg.set('GROUPS','NUMBER',str(len(self.groups))) for group in self.groups: group.updateIni(self._cfg) self._cfg.set('GROUPS', 'SEARCH_GROUPS', encodeintlist([i for i,g in enumerate(self.groups) if g.searchable])) ## Write tables if self.has_zip: self._cfg.set('TABLES','ZIP_TABLE', ' '.join(map(str, self._ziptables))) else: self._cfg.set('TABLES','ZIP_TABLE', '-1') if self.has_marine: self._cfg.set('TABLES','MARINE_TABLE', ' '.join(map(str, self._marinetables))) ## Write layers self._laycfg.writecfg(self._cfg, self) ## Write POI info if self._poigroup != None: self._poiconfig.writecfg(self.poicfg, self) self._cfg.set('LAYERS', 'POI_INDEX', str(len(self.groups))) else: self._cfg.set('POI','POI_CONFIG','') self._cfg.set('LAYERS', 'POI_INDEX', str(-1)) if self._db: self._cfg.set('LAYERS', 'DB_NAME', self._db.name) if self.maptype: self._cfg.set('MAP_INFO', 'MAPTYPE', self.maptype) ## Write routing config if self.maptype == MapTypeStreetRoute: self.routingcfg.writecfg(self._cfg, self) # Write bounding box and bounding rectangle bboxrec = self.bboxrec if bboxrec: self._cfg.set('MAP_INFO', 'BND_BOX', " ".join(map(str,[bboxrec.c1[0],bboxrec.c2[0],-bboxrec.c1[1],-bboxrec.c2[1]])) ) if self._boundrec: self._cfg.set('LIMITRECTANGLE', 'BOUNDRECT', " ".join(map(str,[self._boundrec.c1[0],self._boundrec.c2[0], self._boundrec.c1[1],self._boundrec.c2[1]])) ) ## Copyright holders for i,layer in enumerate(self._copyrightholders): self._cfg.set('COPYRIGHT', str(i+1), layer) def _initcfg(self): """Populate the ini file""" self._cfg.add_section('MAP_INFO') self._cfg.add_section('GROUPS') self._cfg.add_section('LAYERS') self._cfg.add_section('TABLES') self._cfg.add_section('POI') self._cfg.add_section('LIMITRECTANGLE') self._cfg.add_section('COPYRIGHT') self._cfg.add_section('COLORS4BITS') @property def mapnumstr(self): if self.hasmultiplemaps: return '%02d'%self._mapnumber else: return '' @property def inifilename(self): return self._inifile def __del__(self): self.close()