source: tmcsimulator/trunk/webapps/visualizer/data_layers/prep_postmiles.py @ 516

Revision 516, 11.0 KB checked in by jdalbey, 6 years ago (diff)

Add new web app: Traffic Events Visualizer

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