source: tmcsimulator/trunk/src/cptms/prep_postmiles.py @ 284

Revision 284, 11.1 KB checked in by jdalbey, 7 years ago (diff)

Create new folder fo CPTMS including html, bash, and python scripts, and data files

Line 
1import collections,math,sys
2# Scan the postmile file to preprocess it to identify N/S pairs
3# that are so close they will overlap in the display.
4# Compute the perpendicular vector that will be used to adjust their position.
5# Input filename is a Output to stdout.
6# This program adds one extra column to the output, a color, to allow the
7# results to be easily converted to json and displayed for visual verification.
8# (See the convert csv to json bash/awk script)
9# Before being used in the simulation, remove the last column:
10#     cut -f1-6 -d"," output.txt
11
12# jdalbey Feb 2019
13postmileFile = "d12_vds_uniq_sorted.csv"
14
15# Helper function to find the perpendicular unit vector to the line
16# between two postmiles.
17def findPerpX(ax,ay,bx,by):
18    dx = float(bx) - float(ax);
19    dy = float(by) - float(ay);
20   
21    dist = math.sqrt(dx * dx + dy * dy);
22    try:
23        normX = dy / dist;  # calc a unit vector
24        normY = -dx / dist;
25        return round(normX, 6)
26    except ZeroDivisionError:
27        print "Oops, (",ay,",",ax,") appeared twice,"
28        print "causing findPerp() to divide by zero. Probable cause: duplicates in input"
29        print "Please correct the input file."
30        sys.exit(-1)
31# And same for Y ... I know it's redundant code.
32def findPerpY(ax,ay,bx,by):
33    dx = float(bx) - float(ax);
34    dy = float(by) - float(ay);
35   
36    dist = math.sqrt(dx * dx + dy * dy);
37   
38    normX = dy / dist;  # calc a unit vector
39    normY = -dx / dist;
40    return round(normY, 6)
41
42orientationLookup = {'N':0,'S':1,'E':0,'W':1}
43
44def loadHighways():
45
46#    f = open("pm_coords_uniq_sorted.csv",'r')
47    f = open(postmileFile,'r')
48    lines = [line.split(',') for line in f.readlines()]
49   
50    # Create a set containing just the route numbers
51    routeNums = set()
52    for item in lines:
53        routeNums.add(int(item[0]))
54    # put the route numbers in order
55    sortedRoutes = sorted (routeNums)   
56    # Create the empty postmile collections
57    for route in sortedRoutes:
58        #print route,
59        highways[str(route)]=[collections.OrderedDict(),collections.OrderedDict()]
60    #print
61    # Process all the data, placing it in proper route and collection
62    for item in lines:
63        route = item[0]
64        orientation = orientationLookup[item[1]]
65        postmileItem = item[2]
66        highways[route][orientation][postmileItem]=item
67
68
69def dumpHighways():
70    # Dump the highways data we've organized
71    for item in highways:
72        for cnt in [0,1]:
73            list1 = highways[item][cnt]
74            print "highway",item,list1
75            # show fields for one entry
76            for pm_entry in list1:
77                print pm_entry,list1[pm_entry]
78
79def calcPerpendicularVectors(theList,thePerps,dirSign):
80    size = len(theList)
81    idx = 1
82    while (idx < size-1):
83        # see which is closer, previous or next
84        a = abs(float(theList[idx][2]) - float(theList[idx+1][2]))
85        b = abs(float(theList[idx][2]) - float(theList[idx-1][2]))
86        if ( a<b ):
87            #print "closest to ",theList[idx][1]+theList[idx][2]," is ", theList[idx+1][2],
88            ax=theList[idx][4] #long
89            ay=theList[idx][3] #lat
90            bx=theList[idx+1][4]
91            by=theList[idx+1][3]
92            px = findPerpX(ax,ay,bx,by) * dirSign
93            py = findPerpY(ax,ay,bx,by) * dirSign
94            # TODO: ADd check to see if this pm already assigned px,py
95            thePerps[theList[idx][2]] = [px,py]
96            thePerps[theList[idx+1][2]] = [px,py]
97            #print px,py
98        else:
99            #print ">closest to ",theList[idx][2]," is ", theList[idx-1][2],
100            ax=theList[idx][4]
101            ay=theList[idx][3]
102            bx=theList[idx-1][4]
103            by=theList[idx-1][3]
104            px = findPerpX(bx,by,ax,ay) * dirSign # reverse order so normal stays NB
105            py = findPerpY(bx,by,ax,ay) * dirSign
106            # TODO: ADd check to see if this pm already assigned px,py
107            thePerps[theList[idx][2]] = [px,py]
108            thePerps[theList[idx-1][2]] = [px,py]
109            #print px,py
110        idx += 1
111
112    # Did first and last spots get filled?
113    if theList[0][2] in thePerps:
114        #print "good, the first item ",theList[0][2]," is present"
115        pass
116    else:
117        #print "oops, first item ",theList[0][2]," missing"
118        # I'm too lazy to calc this value, so providing zero meaning no perp vector
119        # Which means if by chance this spot has a "mate" then it won't get adjusted
120        # as it should.  TODO: fix this by using the neighbor as adjacent
121        thePerps[theList[0][2]]=[0,0]
122    if theList[idx][2] in thePerps:
123        #print "good, the last item ",theList[idx][2]," is present"
124        pass
125    else:
126        #print "oops, last item ",theList[idx][2]," is missing"
127        thePerps[theList[idx][2]]=[0,0]
128
129# ------------------------------------------------------------------------------------------
130
131highways = collections.OrderedDict()   
132loadHighways()
133
134
135# Iterate over all the highway routes
136for route in highways:
137    #print "Starting route: ",route
138    # ---------------------------------------------------------------------------------
139    # ## First, compute the perpendicular vectors for each item
140    # Compute nearest adjacent for each item (in SAME direction)
141    # We create separate north/south lists so it's easier to locate an adjacent spot
142    northlist = list (highways[route][0].values())
143    southlist = list (highways[route][1].values())
144    northPerps = {}  # a dictionary addressed by postmile that yields perp vectors
145    southPerps = {}
146    #print "northsize is ",northSize, "southsize is ",southSize
147    calcPerpendicularVectors(northlist,northPerps, +1)
148    calcPerpendicularVectors(southlist,southPerps, -1)
149   
150    # -------------------------------------------------------------------------------
151    #print "*****"
152    #print "Perps computed for these up pm's:",sorted(northPerps)
153    #print "Perps computed for these down pm's:",sorted (southPerps)
154   
155    # Try to find matching pairs
156    # Create a match list and add matching postmiles to it.
157    north = highways[route][0]
158    south = highways[route][1]
159    matches = []
160    for item in north:
161        # if south ALSO has item add i to matches list
162        if item in south:
163            #print "match for: " + item
164            matches.append(item)
165   
166    #print "found ",len(matches)," matches."
167    #outFile = open("pairedDots.json","w") # put results here
168    #[0xFF0000,0xFF4000,0xFF8000,0xFFBF00,0xFFFF00,0x00FF00,0x00FFFF,0x0080FF,0x0000FF,0x8000FF,0xFF00FF]
169    colorcode = ["red","salmon","deeppink","coral","orangered","yellow","khaki","purple",
170    "slateblue","lime","lightgreen","cyan","blue","slategray"]
171    colorindex=0
172    for match in matches:
173        # We want to output these as json with matching color
174        id = south[match][0] + " " + south[match][1] + " " + south[match][2]
175        print ("%s,%s,%s,%s,%s,%s,%s" % (id,south[match][3],south[match][4],south[match][5].rstrip(),southPerps[south[match][2]][0],southPerps[south[match][2]][1],colorcode[colorindex]))
176        # lookup perpvector for postmile = south[match][2]
177        #perpx = southPerps[south[match][2]][0]
178        #perpy = southPerps[south[match][2]][1]
179        #print perpx, perpy
180       
181        id = north[match][0] + " " + north[match][1] + " " + north[match][2]
182        print ("%s,%s,%s,%s,%s,%s,%s" % (id,north[match][3],north[match][4],north[match][5].rstrip(),northPerps[north[match][2]][0],northPerps[north[match][2]][1],colorcode[colorindex]))
183        colorindex = (colorindex+1) % 14 # advance to next color
184        # remove them from future consideration
185        south.pop(match)
186        north.pop(match)
187       
188
189    # -----------------------------------------------------------------------
190    leftover_count = len(north)+len(south)
191    #print "Leftover count:",leftover_count
192    # After we've handled all the "matching" pairs of N/S dots
193    # There will be "leftover" single dots
194    if (leftover_count > 0):
195        #print "Leftovers ... single dots"
196        #print len(north),"North keys", north.keys()
197        #print len(south),"South keys", south.keys()
198       
199         
200        # Assert: there are no matching keys in the two dictionaries,
201        #         they should have been removed by the previous step.
202        # Merge the two sets of keys into one list,
203        # each entry is the postmile and direction
204        mergedKeys = []
205        upLetter = ''
206        downLetter = ''
207        if len(north) > 0:
208            upLetter = north.items()[0][1][1]
209            for item in north.keys():
210                mergedKeys.append(item + upLetter)
211            #print "north keys after merging and letter assigned:",mergedKeys
212        if len(south) > 0:
213            downLetter = south.items()[0][1][1]
214            for item in south.keys():
215                mergedKeys.append(item + downLetter)
216       
217       
218        #Sort the list of keys in ascending order by postmile
219        leftovers = sorted(mergedKeys)
220        # Create a dictionary of postmiles and assigned color, and init to white
221        pm_colors=collections.OrderedDict()
222        for pm in leftovers:
223            pm_colors[pm] = "white"
224       
225        # Look for adjacent items close together in opposite directions
226        # Give them same color dots and assign perpendicular vectors
227        close_count = 0
228        prev = leftovers.pop(0)
229        prev_dir = prev[-1:]
230        prev_pm = prev[:-1]
231        for curr in leftovers:
232            curr_dir = curr[-1:]
233            curr_pm  = curr[:-1]
234            # Only consider adjacent items in OPPOSITE directions
235            if curr_dir != prev_dir:
236                curr_color = "white"
237                # See if they are close enough to be considered a pair
238                if (abs(float(curr_pm) - float(prev_pm)) <= 0.111):
239                    #print "FOUND CLOSE: ",prev, curr
240                    close_count += 1
241                    # tag the previous item with a colored dot
242                    pm_colors[curr_pm+curr_dir]="lime"
243                    pm_colors[prev_pm+prev_dir]="lime"
244               
245            prev = curr
246            prev_dir = curr_dir
247            prev_pm = curr_pm
248               
249        #print "Counted ",close_count," close pairs"
250        # print all the tagged items as csv with their tagged color
251        for spot in pm_colors:
252            curr_dir = spot[-1:]
253            curr_pm  = spot[:-1]
254            if (curr_dir == upLetter):
255                curr_info = north[curr_pm]
256                curr_perpx = northPerps[north[curr_pm][2]][0]
257                curr_perpy = northPerps[north[curr_pm][2]][1]
258            else:
259                curr_info = south[curr_pm]
260                curr_perpx = southPerps[south[curr_pm][2]][0]
261                curr_perpy = southPerps[south[curr_pm][2]][1]
262            id = curr_info[0] + " " + curr_info[1] + " " + curr_info[2]
263            # white dots have no mate so don't need to be adjusted
264            if (pm_colors[spot]=="white"):
265                px=0
266                py=0
267            else:
268                px=curr_perpx
269                py=curr_perpy
270            # output this spot
271            print ("%s,%s,%s,%s,%s,%s,%s" % (id,curr_info[3],curr_info[4],curr_info[5].rstrip(),px,py,pm_colors[curr_pm+curr_info[1]]))
272           
273
274   
Note: See TracBrowser for help on using the repository browser.