diff --git a/README.md b/README.md index ff7e979..47e340b 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ to use the tmc_parser block you need to download the TMC location table for your links: [wikipedia info](https://en.wikipedia.org/wiki/Traffic_message_channel#TMC_services_in_operation) finland [download here](http://www.liikennevirasto.fi/web/en/open-data/materials/tmc-location-data) -germany: [request here](http://www.liikennevirasto.fi/web/en/open-data/materials/tmc-location-data) +germany: [request here](http://www.bast.de/DE/Verkehrstechnik/Fachthemen/v2-LCL/location-code-list.html) sweden: [request here](http://www.trafikverket.se/en/startpage/operations/Operations-road/Traffic-information/The-Swedish-Location-Table-for-TMC/tmc-download-page/) norway: [download here](http://www.vegvesen.no/en/professional/Technology/RDS+TMC) italy: [download here](http://www.cciss.it/portale/cciss.portal?_nfpb=true&_windowLabel=quicklinks_1&quicklinks_1_actionOverride=%2Fportlets%2Fquicklinks%2FgoRdsTmc) @@ -77,3 +77,4 @@ set the work directory of the "RDS parser Table" block as the full path (~ short forked from https://github.com/bastibl/gr-rds Continuation of gr-rds on BitBucket (originally from Dimitrios Symeonidis https://bitbucket.org/azimout/gr-rds/ and also on CGRAN https://www.cgran.org/wiki/RDS). +contains code from https://github.com/windytan/redsea diff --git a/python/tmc_classes.py b/python/tmc_classes.py index b276860..233dfdb 100644 --- a/python/tmc_classes.py +++ b/python/tmc_classes.py @@ -31,6 +31,14 @@ from bitstring import BitArray import copy,csv,code +from collections import namedtuple +#Street = namedtuple("Street", "name lcn") +#Street(lcn=12,name="test") +#Street(name='test', lcn=12) +#Street(lcn=12,name="test").name + + + language="de"#currently supported: de, en (both partially) SUFFIXES = {1: 'st', 2: 'nd', 3: 'rd'} @@ -45,16 +53,29 @@ def ordinal(num): return str(num) + suffix class lcl: def __init__(self,lcldir): - self.points= self.dat_to_dict(lcldir+'POINTS.DAT','ISO 8859-15','LCD') - self.poffsets= self.dat_to_dict(lcldir+'POFFSETS.DAT','ISO 8859-15','LCD') + try: + #self.points= self.dat_to_dict(lcldir+'POINTS.DAT','ISO 8859-15','LCD') + #self.poffsets= self.dat_to_dict(lcldir+'POFFSETS.DAT','ISO 8859-15','LCD') self.names= self.dat_to_dict(lcldir+'NAMES.DAT','ISO 8859-15','NID') - self.roads=self.dat_to_dict(lcldir+'ROADS.DAT','ISO 8859-15','LCD') - self.segments=self.dat_to_dict(lcldir+'SEGMENTS.DAT','ISO 8859-15','LCD') - self.allocated_codes=self.dat_to_dict(lcldir+'LOCATIONCODES.DAT','ISO 8859-15','LCD') - self.areas=self.dat_to_dict(lcldir+'ADMINISTRATIVEAREA.DAT','ISO 8859-15','LCD') + #self.roads=self.dat_to_dict(lcldir+'ROADS.DAT','ISO 8859-15','LCD') + #self.segments=self.dat_to_dict(lcldir+'SEGMENTS.DAT','ISO 8859-15','LCD') + #self.allocated_codes=self.dat_to_dict(lcldir+'LOCATIONCODES.DAT','ISO 8859-15','LCD') + #self.areas=self.dat_to_dict(lcldir+'ADMINISTRATIVEAREA.DAT','ISO 8859-15','LCD') + self.allocated_codes=self.dat_to_tuple_dict(lcldir+'LOCATIONCODES.DAT','ISO 8859-15','Code') + self.points= self.dat_to_tuple_dict(lcldir+'POINTS.DAT','ISO 8859-15','Point') + self.poffsets= self.dat_to_tuple_dict(lcldir+'POFFSETS.DAT','ISO 8859-15','POffset') + self.roads=self.dat_to_tuple_dict(lcldir+'ROADS.DAT','ISO 8859-15','Road') + self.segments=self.dat_to_tuple_dict(lcldir+'SEGMENTS.DAT','ISO 8859-15','Segment') + self.areas=self.dat_to_tuple_dict(lcldir+'ADMINISTRATIVEAREA.DAT','ISO 8859-15','Area') #code.interact(local=locals()) + except IOError as e: + print(e) + print("location table not found") def lcn_allocated(self,LCN): - return bool(self.allocated_codes[LCN]["ALLOCATED"]) + if self.allocated_codes.has_key(LCN): + return self.allocated_codes[LCN].ALLOCATED==u"1" + else: + return False def get_poffsets(self,LCD): return self.poffsets[LCD] def get_segment(self,LCD): @@ -67,6 +88,18 @@ class lcl: return self.points[LCD] def get_name(self,NID): return self.names[NID]["NAME"] + def dat_to_tuple_dict(self,filename,encoding,tuple_name): + csv_reader = csv.reader(open(filename), delimiter=';', quotechar='"') + header=csv_reader.next() + ret_dict={} + tupleClass=namedtuple(tuple_name," ".join(header)) + for row in csv_reader: + # decode ISO 8859-15 back to Unicode, cell by cell: #TODO read encoding from README.DAT + unirow=[unicode(cell, encoding) for cell in row] + linetuple=tupleClass(*unirow)# "*" unpacks the list + lcn=int(linetuple.LCD) + ret_dict[lcn]=linetuple + return ret_dict def dat_to_dict(self,filename,encoding,id_col_name): csv_reader = csv.reader(open(filename), delimiter=';', quotechar='"') header=csv_reader.next() @@ -203,18 +236,19 @@ class tmc_area: self.is_valid=False if self.lcl_obj.lcn_allocated(lcn): if self.lcl_obj.areas.has_key(lcn): - loc_dict=self.lcl_obj.areas[lcn] - self.ltype=loc_dict[u'CLASS']+loc_dict[u'TCD'] + area=self.lcl_obj.areas[lcn] + self.ltype=area.CLASS+area.TCD try: - self.subtype=int(loc_dict[u'STCD']) + self.subtype=int(area.STCD) except ValueError:#should not happen, all rows have int self.subtype=0 - print("location subtype %s is invalid in location %i"%(loc_dict[u'STCD'],lcn)) + print("location subtype %s is invalid in location %i"%(area.STCD,lcn)) loc_dict=self.lcl_obj.areas[lcn] - self.roadnumber=loc_dict['ROADNUMBER'] - if not loc_dict['NID']==u"": - self.name=self.lcl_obj.get_name(int(loc_dict['NID'])) + if not area.NID==u"": + self.name=self.lcl_obj.get_name(int(area.NID)) self.is_valid=True + elif self.tableobj.log or self.tableobj.debug: + print("area not found %i"%lcn) elif self.tableobj.log or self.tableobj.debug: print("lcn not allocated %i"%lcn) class tmc_segment: @@ -228,25 +262,28 @@ class tmc_segment: self.second_name="" self.is_valid=False if self.lcl_obj.lcn_allocated(lcn): - loc_dict=None + segment=None if self.lcl_obj.segments.has_key(lcn): - loc_dict=self.lcl_obj.segments[lcn] + segment=self.lcl_obj.segments[lcn] elif self.lcl_obj.roads.has_key(lcn): - loc_dict=self.lcl_obj.roads[lcn] - if not loc_dict==None: - self.ltype=loc_dict[u'CLASS']+loc_dict[u'TCD'] + segment=self.lcl_obj.roads[lcn] + elif self.tableobj.log or self.tableobj.debug: + print("segment/road not found %i"%lcn) + code.interact(local=locals()) + if not segment==None: + self.ltype=segment.CLASS+segment.TCD try: - self.subtype=int(loc_dict[u'STCD']) + self.subtype=int(segment.STCD) except ValueError:#should not happen, all rows have int self.subtype=0 - print("location subtype %s is invalid in location %i"%(loc_dict[u'STCD'],lcn)) - self.roadnumber=loc_dict['ROADNUMBER'] - if not loc_dict['RNID']==u"": - self.roadname=self.lcl_obj.get_name(int(loc_dict['RNID'])) - if not loc_dict['N1ID']==u"": - self.first_name=self.lcl_obj.get_name(int(loc_dict['N1ID'])) - if not loc_dict['N2ID']==u"": - self.second_name=self.lcl_obj.get_name(int(loc_dict['N2ID'])) + print("location subtype %s is invalid in location %i"%(segment.STCD,lcn)) + self.roadnumber=segment.ROADNUMBER + if not segment.RNID==u"": + self.roadname=self.lcl_obj.get_name(int(segment.RNID)) + if not segment.N1ID==u"": + self.first_name=self.lcl_obj.get_name(int(segment.N1ID)) + if not segment.N2ID==u"": + self.second_name=self.lcl_obj.get_name(int(segment.N2ID)) self.is_valid=True elif self.tableobj.log or self.tableobj.debug: print("lcn not allocated %i"%lcn) @@ -257,7 +294,7 @@ class tmc_location: else: try: loc_dict=self.lcl_obj.get_area(lcn) - loc_name=self.lcl_obj.get_name(int(self.loc_dict['N1ID'])) + loc_name=self.lcl_obj.get_name(int(self.point.N1ID)) aref=int(loc_dict[u'POL_LCD']) return(self.__ref_locs(aref,name_string+","+loc_name)) except KeyError:#no area with lcn @@ -275,38 +312,40 @@ class tmc_location: self.loc_dict={} self.has_koord=False self.linRef=None - if self.lcl_obj.lcn_allocated(lcn): + if self.lcl_obj.lcn_allocated(lcn) and self.lcl_obj.points.has_key(lcn): try: - self.loc_dict=self.lcl_obj.get_point(lcn) + self.point=self.lcl_obj.get_point(lcn) self.reflocs=self.__ref_locs(lcn) - self.ltype=self.loc_dict[u'CLASS']+self.loc_dict[u'TCD'] + self.ltype=self.point.CLASS+self.point.TCD try: - self.subtype=int(self.loc_dict[u'STCD']) + self.subtype=int(self.point.STCD) except ValueError:#should not happen, all rows have int self.subtype=0 - print("location subtype %s is invalid in location %i"%(self.loc_dict[u'STCD'],lcn)) - if not self.loc_dict['RNID']==u"": - self.roadname=self.lcl_obj.get_name(int(self.loc_dict['RNID'])) + print("location subtype %s is invalid in location %i"%(self.point.STCD,lcn)) + if not self.point.RNID==u"": + self.roadname=self.lcl_obj.get_name(int(self.point.RNID)) else: self.roadname="" - if not self.loc_dict['N1ID']==u"": - self.first_name=self.lcl_obj.get_name(int(self.loc_dict['N1ID'])) + if not self.point.N1ID==u"": + self.first_name=self.lcl_obj.get_name(int(self.point.N1ID)) else: self.first_name="" - if not self.loc_dict['N2ID']==u"": - self.second_name=self.lcl_obj.get_name(int(self.loc_dict['N2ID'])) + if not self.point.N2ID==u"": + self.second_name=self.lcl_obj.get_name(int(self.point.N2ID)) else: self.second_name="" - if not self.loc_dict['ROA_LCD']==u"": - self.linRef=tmc_segment(int(self.loc_dict['ROA_LCD']),tableobj) - self.negative_offset=self.lcl_obj.get_poffsets(lcn)[u"NEG_OFF_LCD"] - self.positive_offset=self.lcl_obj.get_poffsets(lcn)[u"POS_OFF_LCD"] + if not self.point.SEG_LCD==u"": + self.linRef=tmc_segment(int(self.point.SEG_LCD),tableobj) + elif not self.point.ROA_LCD==u"": + self.linRef=tmc_segment(int(self.point.ROA_LCD),tableobj) + self.negative_offset=self.lcl_obj.get_poffsets(lcn).NEG_OFF_LCD + self.positive_offset=self.lcl_obj.get_poffsets(lcn).POS_OFF_LCD try: #koords stored in WGS84 format with decimal degrees multiplied with 10^5 - self.xkoord=int(self.loc_dict[u"XCOORD"])/100000.0 - self.ykoord=int(self.loc_dict[u"YCOORD"])/100000.0 + self.xkoord=int(self.point.XCOORD)/100000.0 + self.ykoord=int(self.point.YCOORD)/100000.0 self.koord_str="%f,%f"%(self.ykoord,self.xkoord) self.koord_str_google="{lat: %f, lng: %f}"%(self.ykoord,self.xkoord) self.google_maps_link="https://www.google.de/maps/place/%f,%f"%(self.ykoord,self.xkoord) @@ -314,12 +353,13 @@ class tmc_location: except ValueError: self.has_koord=False self.is_valid=True - if not self.loc_dict['POL_LCD']=="":#Europe (lcn==34196) does not have an area reference - self.aref=tmc_area(int(self.loc_dict['POL_LCD']),tableobj) - except KeyError: - print("point '%i' not found"%lcn) + if not self.point.POL_LCD=="":#Europe (lcn==34196) does not have an area reference + self.aref=tmc_area(int(self.point.POL_LCD),tableobj) + except KeyError as e: + print(e) + print("error making point '%i'"%lcn) elif self.tableobj.log or self.tableobj.debug: - print("lcn not allocated %i"%lcn) + print("lcn not allocated or not point %i"%lcn) def get_extent_location(self,loc,extent,direction): #direction: 0:pos, 1:neg if extent==0 or not loc.is_valid: return loc @@ -350,16 +390,18 @@ class tmc_location: def __repr__(self): if not self.is_valid: return "invalid lcn:%i"%(self.lcn) - #elif self.ltype[0:2] == "P1": #junction - elif self.first_name=="":#no first name-> use aref name - name=self.aref - else: - name=self.roadname+","+self.first_name - if self.has_koord: - return "%s,%i:%s, geo:%s"%(self.ltype,self.subtype,name,self.koord_str) - #return '%s,%i:%s, geo:%s'%(self.ltype,self.subtype,name,self.google_maps_link,self.koord_str) else: - return "%s,%i:%s"%(self.ltype,self.subtype,name) + return unicode(self).encode('utf-8')+" lcn:%i"%(self.lcn) + ##elif self.ltype[0:2] == "P1": #junction + #elif self.first_name=="":#no first name-> use aref name + #name=self.aref + #else: + #name=self.roadname+","+self.first_name + #if self.has_koord: + #return "%s,%i:%s, geo:%s"%(self.ltype,self.subtype,name,self.koord_str) + ##return '%s,%i:%s, geo:%s'%(self.ltype,self.subtype,name,self.google_maps_link,self.koord_str) + #else: + #return "%s,%i:%s"%(self.ltype,self.subtype,name) #~ class tmc_location: #~ def __init__(self,lcn,tableobj): #~ self.tableobj=tableobj @@ -716,6 +758,8 @@ class tmc_message: #EventCode: EventText #F #return unicode(text,encoding="utf-8") + elif self.location.is_valid: + print("linRef invalid on LCN %i"%self.location.lcn) return text.encode('utf-8') def __str__(self): return str(self.event.updateClass)+": "+self.getTime()+": "+self.events_string()+"; "+self.multi_str()