Sie sind auf Seite 1von 118

University of Newcastle

Discipline of Mechanical Engineering Final Year Project

Statistical Modelling of Public Roads

Author: Andrew Whalan whalan@whalan.com

Supervisor: Dr. Bill McBride

December 23, 2009

Declaration
I declare that this thesis is my own work unless otherwise acknowledged and is in accordance with the Universitys academic integrity policy available from the Policy Library on the web at http://www.newcastle.edu.au/ policylibrary/000608.html I certify that this assessment item has not been submitted previously for academic credit in this or any other course. I acknowledge that the assessor may, for the purpose of assessing this thesis: Reproduce this thesis and provide a copy to another member of the Faculty; and/or Communicate a copy of this assessment item to a plagiarism checking service (which may then retain a copy of the item on its database for the purpose of future plagiarism checking). Submit the assessment item to other forms of plagiarism checking. I certify that any electronic version of this assignment item that I have submitted or will submit is identical to this paper version.

Student name: Andrew George Whalan Signature:

Acknowledgements
I would like to thank a few key people: Dr. Bill McBride, My supervisor, for taking the time to listen to my ideas, provide feedback on how to form a research project from it and to help me capture the data required for this project. Other academic sta with who have answered my many questions and from whom I have obtained many useful pointers towards solving my problems. Those that drove around with data-loggers in their cars so that I could obtain a decent data set for my research.

ii

Legal Considerations
OpenStreetMap Data
Open Street Map data is made available, and utilised, under the Creative Commons, Attribution-ShareAlike-2.0 licence (CC-By-SA)1 . As the work of this project does not modify or transform the original maps provided, adding additional layers of information to the unchanged OSM model, the output of this project is considered to be a collective work as dened in section 1.a of the licence document. All original map data is available from the OSM website, or on request from the author.

Trademarks
Many trademarks are used in this report to reference products and services that exist in the market-place. They remain the sole property of their owner, and their usage in this document should not be taken to an aliation with any of the products or organisations.

http://creativecommons.org/licenses/by-sa/2.0/

iii

Abstract Many products such as in-car GPS navigation devices and mapping software estimate travel time. It is commonplace for this information to be wildly inaccurate as the models used to develop an estimate fail to compensate for time of day uctuations due to congestion, intersection timing and other factors. An organisation wishing to increase the accuracy of these estimates needs to understand any variations in travel time requirements to provide their clients with the required level of service. The research undertaken develops a methodology to build a exible statistical model to better quantify this variability.

Contents
1 Introduction 1.1 Problem Denition . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Project Outcomes . . . . . . . . . . . . . . . . . . . . . . . . 2 Literature Review 2.1 General . . . . . . . . . 2.2 Online Map Services . . 2.2.1 Google Maps . . 2.2.2 WhereIs.com . . 2.2.3 NineMSN Maps . 2.2.4 Comparison . . . 2.3 Trac-aware GPS Units 2 2 3 4 4 4 4 4 5 5 5 7 7 7 9 10 10 11 12 13 13 14 16 18 20 20 23 23 23 23

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

3 Project Components 3.1 Data Collection . . . . . . . . . 3.1.1 Methodology . . . . . . 3.1.2 Challenges . . . . . . . 3.2 Collation and Storage . . . . . 3.2.1 Database . . . . . . . . 3.2.2 Map Data . . . . . . . . 3.2.3 GPS Data . . . . . . . . 3.3 Data Processing . . . . . . . . 3.3.1 Convert coordinates . . 3.3.2 Validating Map Data . . 3.3.3 Mapping Data . . . . . 3.3.4 Time-cost Allocation . . 3.3.5 Challenges . . . . . . . 3.3.6 Unaddressed Challenges 3.4 Analysis . . . . . . . . . . . . . 3.4.1 Kinematic Analysis . . . 3.4.2 Probability Analysis . . 3.4.3 Challenges . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

4 Results 4.1 GPS Data . . . . . . . . . . . . 4.2 Road and Intersection Analysis 4.3 Sample Route Estimation . . . 4.4 Speeds and Time of day . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

25 25 26 30 34

5 Conclusions 36 5.1 Project Summary . . . . . . . . . . . . . . . . . . . . . . . . . 36 5.2 Future Directions . . . . . . . . . . . . . . . . . . . . . . . . . 36 A Processor Source Code B Data Logger User-Guide C DG-100 Datasheet Bibliography 38 101 107 110

ii

List of Figures
3.1 3.2 3.3 3.4 3.5 3.6 3.7 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 UMPC Prototype . . . . . . . . Database Structure . . . . . . . Intersection Error . . . . . . . . Intersection correction . . . . . Mapping errors . . . . . . . . . Mapping paths . . . . . . . . . Idealised view of velocity prole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . along a road . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 10 14 15 15 17 19 25 26 27 27 28 30 31 31 33 34

GPS Data Collected . . . . . . . . . . . . . . . . Section of road analysed . . . . . . . . . . . . . . Time-cost Frequency Graph . . . . . . . . . . . . Time-cost Cumulative Distribution Function . . Time-cost at Intersection By Sample Number . . Sample route with roads numbered . . . . . . . . CDF for road travel time in sample route . . . . CDF for intersection travel time in sample route CDF for Sample Route travel time . . . . . . . . Time trending of variation in travel time . . . . .

iii

List of Tables
2.1 3.1 3.2 4.1 Comparison of trip estimates . . . . . . . . . . . . . . . . . . Buer Size vs. Load Time Comparison . . . . . . . . . . . . . Algorithm Comparisons . . . . . . . . . . . . . . . . . . . . . Interpolated CDF points along sample route . . . . . . . . . . 6 12 18 32

Chapter 1

Introduction
1.1 Problem Denition

Travel times are something that aect us every day. They aect the decisions we make, and can aect more abstract aspects of our lives such as our mood. Nobody likes to be held up in trac, or arrive at a destination late. Varying travel times are even more of a concern when the travel is part of an commercial venture, where late delivery can mean upset or inconvenienced clients, and possible nancial impacts. There are many ways that time can be estimated for a trip. The simplest method would be to use a heuristic, or rule of thumb. We often travel the same path multiple times, so this level of estimation usually has an acceptable degree of accuracy, but it is not a structured model that can be applied to an arbitrary path. A basic analytical approach that could be applied to any path, would be to consider the distance, the prevailing speed-limits or a reasonable average speed, and by simple arithmetic, work out roughly how long it would take to travel the path. A more complex method would consider the types of roads travelled on, and apply dierent speeds for dierent categories of road. As the complexity level of our model increases, it is expected to be met with an increased level of accuracy in predictions. As most road users would be all too aware, there are great variations in travel time. This is often dependent on time of day changes, with some sections of roads, more severely aected than others. As such, a model that is expected to give any level of accuracy must take into account these time uctuations and adjust accordingly. The problem that we will solve is to provide an accurate time estimate for a given path, based on a statistical condence interval.

1.2

Project Outcomes

The outcome of this project will be to dene a suitable methodology to build a statistical model of a road network. Due to the large number of roads and intersections required to be modelled, the model will need to be generated automatically from the input data. Much of the eort of this project will be in the development of a rst-pass toolkit of software to perform this modelling automatically.

Chapter 2

Literature Review
2.1 General

Through consulting various sources of information, it became apparent that using real GPS data to build a statistical model was a novel approach to understanding travel times and trac ow. A key source of information about a more traditional approach has been Urban Transportation Modelling and Planning [SM75].

2.2
2.2.1

Online Map Services


Google Maps

Google oers a web-based mapping service on its website http://maps. google.com.au. Communications are under-way to try to better understand, if possible, the algorithms and methodology used to generate their time estimates.

2.2.2

WhereIs.com

Whereis (a division of Sensis Pty Ltd, a fully-owned subsidiary of Telstra Ltd) provides an online mapping service from their website http: //whereis.com, in addition to supplying maps for use by in-car GPS units. Whereis clearly identies on their website1 that they use a static speed model for all roads, and ignore the impact of intersections, rather using lowered road speeds to compensate for this: The estimated travel times for map routing are calculated using average travel speeds dependent on the road classication and mode of transport.
1

http://www.whereis.com/products/terms-of-use/index.html

No consideration is made for trac conditions, trac lights, or other barriers and factors that may otherwise impede or enhance the estimated travel time. The documentation continues and outlines the static speeds that they use for dierent categories of roadways: Main roads: 90km/h Roads of major importance: 60km/h Other major roads: 50km/h Secondary roads: 50km/h Local connecting roads: 40km/h Local roads: 40km/h Local roads of minor importance: 35km/h Other roads: 25km/h Based on these values, the analysing of a trip along a section of freeway with speed limit of 110km/hr, shows that this method of estimation will over-estimate the time by approximately 20%. Similarly, with other roads where there is a signicant dierence between the posted speed limit, and the estimated speed used, there will be noticeable error.

2.2.3

NineMSN Maps

NineMSN operates a map service as part of their web portal. It can be found at http://maps.ninemsn.com.au/. No information is provided on their website as to what estimation method is used to calculate trip times.

2.2.4

Comparison

To quantify the relative accuracy of estimates from key providers, a route from Singleton to Newcastle was entered into each system, and the results presented in table 2.1. As can be seen from the data, there is a variance of 16 minutes or 23% between Google and NineMSN estimates.

2.3

Trac-aware GPS Units

Major GPS vendors integrate real-time trac information to their higherend GPS Units to better handle congestion, accidents road works and other events to improve the accuracy of trip time estimations. These do so by 5

Provider Google Maps Whereis NineMSN

Test Route 1hr 25min 1hr 13min 1hr 9min

Table 2.1: Comparison of trip estimates

wirelessly sending the information to the units that use the data to provide both updated time estimates, and in some cases, alternative routes to optimise the travel time. Analysing how great an impact this would have on travel times is largely a subjective analysis based on the trips chosen at particular times, but would serve to improve travel time estimates. The major data provider in Australia is SUNA2 who provides trac information for most GPS vendors using a digital radio service. This service is available in Brisbane, Gold Coast, Sydney, Melbourne, Perth, Adelaide and Canberra. Based on this service area, 55% of the population is covered. SUNAs website states3 that they only ... transmit trac updates for major arterial roads, freeways, and tollways. Various GPS vendors re-brand this service as their own product, seamlessly integrating the information with the GPS units interface. In addition to the SUNA service provided through their GPS units, TomTom also provides a value-added service called TomTom Plus using the data network provided by mobile phone companies (GPRS, UMTS). This would appear to be an attempt to address the transmission limitations of the digital radio broadcasts used by SUNA, rather than expand the trac information provided. The key weakness of this system is that if you are not in a covered city you will have no congestion related information for trip planning and updates. The other major dierence is that this system uses map data combined with real-time data, where as this project uses map data combined with historical data.

2 3

http://www.sunatraffic.com.au/ http://www.sunatraffic.com.au/coverage.htm

Chapter 3

Project Components
3.1
3.1.1

Data Collection
Methodology

The rst step to building any statistical model is to collect the data that will be used to create the model. To ensure we obtained the right resolution of data, a device to log current position and optionally speed from GPS data at 1 second intervals (1Hz) was required. To serve as an initial prototype, an ASUS R2H Ultra Mobile PC was purchased for approximately $650, sourced second-hand from eBay. This device is a small computer (234mm x 132mm x 33mm WHD) shown in g. 3.1. It has an integrated touch screen, GPS receiver and wireless networking (WiFi and Bluetooth) making it full all the possible requirements for our project.

Figure 3.1: UMPC Prototype The device was initially congured using Ubuntu Linux 8.10. Chal7

lenges were encountered with the installation of the operating system. The key challenges being with managing the state of the WiFi radio and issues with the video card chipset. The WiFi conguration issues were resolved by manually conguring the hardware and wireless settings. Fixing the video issues required corrections to be made to the video card driver. To alert the appropriate parties to this, a bug was led online1 . Conguration information was passed upstream and local testing was done as various updates were released to support the eorts of the developers until the issue was resolved. After establishing the initial conguration of the device, it was used to collect data whenever it was switched on. This information was logged to the internal hard drive and manually downloaded and converted to a common format for storage. A prototype environment was developed which can be seen displayed on the screen of the device in gure 3.1. This environment provided a framework for future development and the early development of it allowed a user to toggle GPS logging on and o without the need to understand how to manually instruct the device to log the data. Due to its cost, lack of availability and size, it was apparent that a cheaper, more compact, well supported and readily available device was required. Searching online showed that the GlobalSat DG-100 data logger fullled these criteria. Costing approximately $100 ea including postage, 5 devices were ordered. A datasheet for the device can be found in appendix C. Delays in getting device resulted in the initial order of the device being cancelled, and reordered from another supplier. The DG-100 integrates easily with other technology. It uses rechargeable AA Ni-MH batteries as its power-source which are rechargeable by leaving the device plugged into a powered USB port after downloading the data, avoiding the need to manually charge or change the batteries of the device. The device is 80mm x 70mm x 18mm so it is compact enough for a majority of applications. Also supported is an external GPS antenna via a standard MMCX connector. The internal antenna proved to be powerful enough to not require an external antenna for this project. Internally the DG-100 uses the well supported Prolic USB to RS232 chipset. The bundled software had problems downloading data from the device when it was completely full. By switching to the open source program GPSBabel, we were able to download the data from the device no matter how full it was. One of the devices was kept to collect data, 3 were distributed to others to help collect data. The 5th was kept as a spare if any of the devices failed. The use of the device was explained to each person, and a user-guide (shown in appendix B) was also developed and distributed to users to reinforce what was discussed and provide a point of reference if they had
1

https://bugs.launchpad.net/ubuntu/+source/xserver-xorg-video-intel/+bug/312133

problems.

3.1.2

Challenges

The battery life of DG-100 units was quite limited, being powered by only 2 AA Ni-MH batteries. While logging continuously at 1 second intervals, the battery life was approximately 20 hrs. When the unit was congured to stop logging when stationary, the battery life was extended by approximately 10 hours. By incorporating the use of a USB power supply that was powered by the 12 volt cigarette lighter socket, the unit was provided with enough charge for continuous operation provided the car was operated for a few hours each day. Due to the low power requirements of the device, even from at, it had sucient charge to operate after the USB power was activated. The device is listed as having storage capacity of 60,000 points when storing only the location. For continuous operation, logging position and speed at one second intervals, the device could store approximately 12 hours of information. As most cars and hence the devices were stationary for a vast majority of this time, the devices were recongured to only log when the speed of the device was above 3 Km/hr. This change, coupled with the USB power supply allowed the device to operate as required for over a week at a time greatly reducing the need for constant manual interaction with the device. Another key challenge to collecting information was the requirement for manual interaction with the device. Firstly, users had to remember to ensure the device was in the car they were using, plugging it into the USB power supply. If the device was o they had to manually turn it on. Once the device was full, they had remove the device from the car, manually download the data from the device, and email it. To address these issues, a custom car-mounted PC could be implemented that would provide all the required functionality, while automating every step of the process. By eliminating the manual components in this process, more data could be collected and automatically uploaded into the system, maximising the amount of data-collection. This hardware infrastructure could then be further expanded into an intelligent device to provide a GPS unit that also automatically receives updates, along with recording other car information. On a cheaper scale of improvements, the DG-100 hardware may be able to be modied to automatically power on and o with the ignition. Software could be developed to automatically download the data from the device and then upload it to a central repository. This would make using the device easier for end users.

3.2
3.2.1

Collation and Storage


Database

MySQL was used as a database backed for the map and GPS data. MySQL was chosen as it is Open-Source, free of cost, and it runs on all major operating systems which would be helpful should this project be developed further. The current version of MySQL supports multi-terabyte tables, so supports any volume of data stored and processed as part of this process.
map_way_tag id way_id k v map_node_tag id node_id k v model_way_list model_id way_id model_node_list model_id node_id model_intersection id model_id node_id model_road id model_id way_id node_from node_to sequence_from sequence_to stat_intersection id model_id intersection_id from_road to_road timestamp cost stat_road id model_id road_id timestamp cost raw_point id dataset_id point_time lat lon speed northing easting

map_way id is_loop length map_way_point way_id node_id sequence map_node id lat lon northing easting

device id dataset id upload_time device_id filename remote_ip data_points

model id ctime title lat_min lon_min lat_max lon_max search_width

model_point_list model_id point_id road_id heading

vw_road_segments model_id road_id segment n1_easting n1_northing n2_easting n2_northing dist

Figure 3.2: Database Structure The diagram in gure 3.2 shows the structure of the database used to 10

store the data in the project. Table names are shown as the blue headings of the boxes, and the primary key in each table is shown in pink, all named id. A naming system was used to ensure that the purpose of each table was clearly identied. Table names with the map prex store information from the OpenStreetMap data. The only modication to this data is the caching of eastings and northings in nodes to avoid recalculation. Any boundary or road in the system is referred to by OSM as a way, made up of multiple nodes. Both information types have key-value pairs stored as in their respective tag tables. These tags, among other things, identify which ways are actually roads, along with the name and class of roadway if known. To store information about the model we are generating, the model prex was used. The information used to develop the model, such as splitting up the roads stored in the map tables into segments of road were stored here. A key optimisation that was developed during the course of the project was the use of the tables with the list sux. These tables cached a list of dierent classes of information that would be of interest to the model. By creating a list containing far smaller sets of data, queries could be executed far quicker, even with the additional overhead of re-associating the data with the original source data. The third group of tables is the statistics tables, identied with the stat prex. These tables store discrete entries for the time-cost for intersections and roads that can be then used in an analysis package to determine a statistical distribution for a time estimate. By storing the individual time assignments, along with the time at which they occurred, analysis has the highest quality data, as no aggregation has diluted the information.

3.2.2

Map Data

The maps used were provided by the OpenStreetMap project2 for this project. OpenStreetMap provides a community based contribution approach to developing maps, in a similar fashion to Wikipedia. Anybody who is a registered user can create and modify map data, provided they agree only to source data from legitimate sources, such as their own trips, observations of street signs and so on. Copying map data from other providers is forbidden. Rather than download the entire 7.5 gigabyte3 compressed maps for all of earth, an Australian extract was used that was a 125 megabyte download which expands to approximately 1.2 gigabyte XML data le. Once this data was downloaded, attention turned to loading this information into the database. A custom tool was developed to import the data into the database. The tool was developed in C++ using the Expat XML
2 3

http://openstreetmap.org Based on mirror at http://ftp.heanet.ie/mirrors/openstreetmap.org/

11

Buer Size (bytes) 10K 100K 1M 5M 10M

Load Time 19min 19min 19min 19min 19min 42sec 28sec 40sec 44sec 56sec

Table 3.1: Buer Size vs. Load Time Comparison

parser4 . While there were challenges with learning to eectively use the Expat library, once the initial bugs were resolved, the tool was able to import the data in about 20 minutes. Dierent memory usage congurations were investigated and it was found that the buer size had little impact on the loading time. The results of memory testing is shown in table 3.1. After the import tool was developed, it was discovered that there was already a tool developed by the OSM team, Osmosis. As Osmosis doesnt appear to allow custom database structure, it was useful to have our own tool developed.

3.2.3

GPS Data

GPS Data which was downloaded from the device is stored in the GPX format dened by [F+ 07], was uploads were managed using a web-based front end. This front end was written in PHP and connects to the MySQL database which was used to store all the project data. As the GPX is based on XML, the information was parsed by the built in XML functionality in PHP. A subset of the GPX format was parsed, as specic to this project. The sequence of points loaded from the GPX le contained the latitude, longitude, speed and time at each point in time, usually one second intervals. Due to the limited number of users collecting data, the upload process and database were run locally. Should the system be expanded to however no modications would be required to allow it to function both online and for any number of additional users.
4

http://expat.sourceforge.net/

12

3.3
3.3.1

Data Processing
Convert coordinates

All map data and GPS data is provided in latitude and longitude. While a latitude and longitude pair uniquely identify a point on the surface of the earth, it does not allow for easy analysis. Drawing the content in the database, based on latitude and longitude showed high degrees of distortion due to trying to draw a spherical surface on a at surface without appropriate conversion. It was apparent that there was a need to convert the coordinates into grid coordinates was identied. To convert the coordinates into grid coordinates of units in metres, the Universal Transverse Mercator (UTM) system was used. The algorithm implemented was reverse engineered from a Microsoft Excel spreadsheet provided on the Geoscience Australia website 5 . Additional information was obtained from an information sheet on the UKs Ordnance Surveys website [Sta02]. Once it was veried that the conversions were accurate, an initial stage of processing was implemented to convert all map points and GPS points to their respective eastings and northings. UTM translation of all map data and GPS points took approximately 6 hours of processing time. The conversion was stored in the database so that it only had to be performed once. This conversion allowed later stages of processing to occur with respect to metres for maps and GPS data, and seconds corresponding to the GPS data, standard units for distance, velocity and acceleration. Creating drawings of maps and data was simplied as well, as the information could be represented on a standard Cartesian plane without experiencing any of the previous distortions. The conversions were so accurate that they served to validate the relative accuracy of the GPS velocity recorded. Numerically integrating the area under a velocity versus time graph a value less than 1% dierent to the value given by the distance travelled. The only time numerical integration failed to provide an accurate distance was when the device travelled less than 3 km/hr and thus the speeds for a period of time were unknown. The implementation of the UTM algorithm was included in the class Projection and the source-code can be found in appendix A, in the les Projection.cpp and Projection.h.
5

http://www.ga.gov.au/geodesy/datums/redfearn.xls

13

3.3.2

Validating Map Data

Correcting Intersection Locations From manually comparing the GPS data against the map data, of which an example is shown in gure 3.3, it was apparent that some of the intersections were out by a signicant distance. To correct this an algorithm was devised to help guess the true centre of the intersection.

Figure 3.3: Intersection Error The algorithm was to look for crossing paths near each intersection. The crossing paths needed to have a suciently dierent heading (greater than 70 degrees), and occur at a suitable speed (greater than 1m/s) get the average location of the crossing of paths that are at large degrees of dierence. Figure 3.4 shows the analysis about one particular intersection. The intersection is at point (0,0) and all coordinates are relative to this to provide an oset to use. Based on the average of the coordinates of the path crossings, shown by the red diamond in the gure, the intersection analysed has an oset of 12.7 metres west and 8.6 metres north. The C++ source-code for the implementation of the algorithm used can be found in appendix A in the le ModelGenerator.cpp, starting on line 632.

14

30

25

20

15

10

0 25 20 15 10 5 0 5 10 15

Figure 3.4: Intersection correction Identifying other Errors Three other main classes of errors were identied with respect to mapping points to routes shown and labelled in gure 3.56 .

Figure 3.5: Mapping errors (green), along with mapped data (red), and map information (blue). When GPS points were not within a threshold distance or heading to a path, they were unable to be accurately assigned. These points are identied in gure 3.5 by A. To resolve these points the search width for each road could be expanded, however doing so would mean that search widths for adjacent roads may overlap causing points be assigned to incorrect roads. The preferred solution would to be increase the number of points dening the road so the map would more closely follow the actual path of the road.
6

Figure 3.5 was drawn using latitude and longitude coordinates, and is thus distorted

15

Another source of GPS points that were unable to be mapped accurately were those while a car was stationary or parked in a parking area, usually away from a mapped road. Open Street Map provides the functionality to dene areas as a parking area7 however no attempt was made to utilise this information due to the lack of map denitions of local car-parks, and the relatively low impact of this on the project. This error is identied at point B in gure 3.5. The third error identied in the maps was that of missing roads shown labelled as C. These can be clearly seen when plotting GPS paths over the map, and added using standard OSM tools. Approaches to automatically identifying roads to t the unmapped points were considered however this type of automatic map generation was well outside the scope of the project.

3.3.3

Mapping Data

GPS Data provided us with information on the path taken by a car. We have actual roads and intersections dened by our map data provided from OSM. The challenge with this was to map the two data sets to provide a coherent view on the data. After an initial algorithm proved to be awed, a revised algorithm was developed. A comparison of the features of the algorithms can be found in table 3.2. The initial approach to mapping the GPS path data, point by point to a straight-line section of road between two nodes on the map. This portion of road was referred to internally as a segment. Points had to full two criteria to be mapped to a segment: a distance of 30m tangential to the segment and a orientation aligning within 30 degrees to the orientation of the road. Before the usage of grid coordinates in the system, degree distance was used to approximately nd matching roads. This was quite inaccurate as north and east distances were not scaled evenly, and it was hard to conceptualise what an acceptable distance tolerance was. This was one of the problems that prompted the implementation of UTM projections to use a grid coordinate system. The algorithm used to calculate the tangential distance used the identity that the dot product of the line and its tangent is zero, solving for unknowns. Information regarding this method was found online8 along with code samples in other languages that were used as a point of validation. Algorithm One To associate the path data in this rst algorithm, each intersection and the intersecting GPS paths were considered discretely, using a radius about the intersection to determine which portions of GPS paths to consider. The
7 8

http://wiki.openstreetmap.org/wiki/Tag:amenity%3Dparking http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/

16

point closest to the intersection was found and analysis done about this point. No consideration was given to the actual length of the section of road or roads. Road information leading to and from the intersection was taken from the roads that the points mapped to. This was an error prone approach for several reasons. When the path passed through an intersection, it would often associate with the wrong road as shown in the rst case of gure 3.6. At point B we can see that the path associates to roads on both sides of the intersection, and at point A there are sections of road that are not associated with. Our mapped path is discontinuous. This discontinuity means that in using this algorithm there is no guarantee that the time costs associated with the roads leading to and from this intersection, and the costs associated with the intersection itself, will be assigned.

Figure 3.6: Mapping paths, using two algorithms Another key error with the rst algorithm was that the road identication was based on points mapped. If there were errors with mapping the points to a road, the costs for a path may be assigned to an incorrect road, corrupting the nal results and overall validity of the model generated. The nal aw of this model was that every point needed to be mapped to a segment of road, when this information could be inferred by considering the intersections used. This was also addressed in the second algorithm. Algorithm Two This revised algorithm worked on a continuous mapping of the path to intersections. Once the entire path had its key points mapped to their appropriate intersections, the roads between the intersections were inferred based on the possible paths in-between. This was based on the key assumption that there is only one road between two intersections, which is true for a majority of the road network, and held true at all times with the portion of the road network that was analysed in detail. In a larger scale usage of this system this limitation could be addressed by taking a hybrid approach; 17

Method 1 Path Mapping Time Allocation Map Error Tolerance Road Identication Mapping Required Discontinuous No Guarantee Poor Dependent on mapping accuracy Every Point to a road

Method 2 Continuous Guaranteed High Tolerance Unique path between intersections Only Intersections

Table 3.2: Algorithm Comparisons

additionally considering the direction of travel, and identifying the closest matching road for a path. By using a continuous approach to mapping the data, we are guaranteed to allocate all of the time costs associated with each road and intersection. If time is mis-allocated to a road instead of an intersection, or vica versa, the impact on the overall model will be minimal, particularly in comparison to the rst algorithm. If errors exist in our map data, as discussed in section 3.3.2, this algorithm will handle it gracefully. As shown in the second diagram in gure 3.6, the green dotted line representing our mapped path, closely more closely follows the GPS path shown in orange. While this is far away from the map paths, by following the GPS path closely, we have less error induced in our model. As there are far less intersections than there are GPS points this algorithm can run far faster, particularly for large datasets. Overall, this second algorithm addressed all major concerns from the rst algorithm, improved both execution speed and the accuracy of the model generated. As this was the case, no additional revisions were

3.3.4

Time-cost Allocation

Having roads and intersections to analyse, the method for time-cost allocation for both algorithms outlined in section 3.3.3 was roughly the same. The method used for the second algorithm will be discussed here. The two costs that needed to be calculated were due to the road travel time, and the time taken to traverse the intersection and transition to the next road. The rst step in analysing the section of road is to understand the standard velocity along the road to understand the cost of travelling along the road, irrespective of the intersection related delays. Several methods were used to try to identify the standard velocity along the road, shown as V in gure 3.7. The key methods explored were:

18

Figure 3.7: Idealised view of velocity prole along a road 1. Average of the velocities when they are at 80% or above of the maximum velocities. Vstd = V : where V 0.8Vmax 2. Weighted average based on the inverse rate of change, ie. stable (slower-changing) velocities weighted more heavily. Vstd = (V /V )/(1/V ) : V = dV /dt 3. Set to 95% of the maximum velocity. Vstd = 0.95Vmax Although crude, the 3rd method of determining a standard velocity worked best with the data analysed. The time-cost of the road was then calculated based on this velocity, and the distance of the road using the relationship t = l/V where l is the length of the road. The lower velocities along sections A and B in gure 3.7 represent the costs associated with the previous and following intersection respectively. These points, t1 and t2, are determined as the rst time the velocity is less than Vstd travelling out from the point of Vmax . To calculate the cost associated with each section, if we set lA to be the length of the road along section A, tA = t1 lA /V . The cost of section A is the time taken to travel the section, minus the cost already assigned to the road for the section. Similarly for section B, tB = (tmax t2 ) lB /V . If the point at which the car stops at the intersection is identied after the map intersection due to map errors, then we are still guaranteed that the cost will be correctly assigned to the intersection. Instead of being captured in section B of the road before the intersection, as would usually occur, the time will be captured in section A of the following road. The intersection cost is set to be tB of the road leading into the intersection and tA of the 19

road leading out of the intersection. This cost is also associated with the roads used to travel through the intersection. To ensure all the time was allocated, the times were evenly scaled so that the total time would be allocated. From the sample data set analysed an average of 1.9 seconds was required to be added to the time-costs (standard deviation of 5.4s, median of 1.5s) which represented a average correction of 11.0% (standard deviation 16.0%, median of 5.8% . Shorter roads were more sensitive to timing errors and have an increased likelihood of requiring more aggressive time-cost scaling. Once all sections of roads between intersections were analysed, this information was collated and loaded into the database for each road cost along the path specic intersection cost. This data can then be used for analysis to build the required statistical model.

3.3.5

Challenges

When managing such large sets of data, care had to be taken in fetching information from the database to avoid overloading the available memory in the system. As such, when performing queries on data for which there were many rows, the data was fetched one row at a time, rather than fetching the entire dataset in one operation. Some operations that could be easily split, such as calculating the northings and eastings were split up, so that only 10,000 entries were posted at a time eliminating the memory issues. Once these modications were made, there was no noticeable impact on the memory usage of the application. A specialist statistical package would have served better to analyse the statistical information that came from the model. Using the spreadsheet package, while functional, was quite cumbersome and time consuming to produce results.

3.3.6

Unaddressed Challenges

Route changes As roadways evolve with time, additional routes will become available. The system will use an estimated statistical model based on the new roadway type until a sucient amount of data is present to create a model for the distinct sections of road and intersections aected. Changes that degrade sections of road either permanently, or longer term such as extended road works, will not be separately modelled. By weighting the historical data available to the system such that newer data is given a higher level of importance, these eects on the system may be minimised.

20

Events and Holidays The data used to generate the model is time stamped in GMT9 , data around events on a particular day could conceivably captured and modelled. The model will inherently cater for these situations as a low-probability increase in trip type. As these events are deterministic, consideration will be given to an appropriate way to capture this information. Accidents Due to the open-loop nature of the system, random occurrences that aect the operation of the system will not be specically modelled. However, as many random events follow a particular probability distribution, with enough data, the system will automatically cope with this disturbance. The Roads and Trac Authority in NSW provides a trac reports website10 listing known issue that may aect road users including accidents, events and planned road works. While they do not currently provide a machine readable version of this information, this information could potentially be automatically downloaded from their website. With combination of parsing this information and manual data entry, this information could then be transformed into a format that could automatically feed both the model generator or fed live to an in-car GPS unit. Car and Driver Every driver has a dierent style to their driving which can aect the travel times for their trip. For instance, more cautious drivers will take more time through intersections, where as aggressive drivers are less likely to stop on yellow lights, and accelerate more rapidly after an intersection. With enough data this dierence would be able to be quantied, however in this project there was insucient data to quantify this dierence. Weather Similar in nature to events and holidays, weather will play a role in the trip times. As weather conditions are not deterministic, and are location specic, the model relies on the general impact on to the probability distributions of travel times. Further investigation into this however is warranted. The Bureau of Meteorology provides much of this information on their website and future projects could attempt to identify ways in which this information may be
9 10

Greenwich Mean Time http://www.rta.nsw.gov.au/trafficreports/index.html

21

incorporated. Considering the time of the year that the data is collected may also provide an increased level of accuracy. Map Data and Errors Other key issue that was encountered with respect to map data was the way round-abouts were handled. In the map data the are stored as a sequence of very short roads that formed a loop. As our sample area didnt contain any round-abouts mapped like this and as such it was not an issue for this project. However, should round-abouts be required to be handled in the future, each point on the round-about could simply be treated as the same point, eectively forming a normal intersection. From the data analysis undertaken as part of the project, it was noted that many intersections do not align with the GPS data gathered. Other mapping errors such as the incorrect location of roads, missing roads, and roads for which there was limited or no supporting information such as name. Algorithm Flaws When the program to process our map data was rerun, the internal reference numbers for each segment of road was renumbered. This meant that it was cumbersome to compare results as all the information had to be correlated back to map data to provide a consistent view on the data. This could be addressed in future versions of the software, or be dealt with by improve automation and functionality in the statistical analysis tools.

22

3.4
3.4.1

Analysis
Kinematic Analysis

As part of the execution of the program for processing the data, data was grouped and output. By capturing and ltering the output we were able to analyse both the data being loaded by the system, and the accuracy with which the data was processed. The data was analysed with respect to 3 key parameters: velocity, distance to/from intersection and time. Filtering the data was achieved by standard Linux commands: grep, cut and sed. These commands lter lines of output, select specic columns of data and replace data as needed respectively. They can be found on all standard Linux machines and are all open source. Implementations of these tools have existed for many decades on other POSIX (Unix) systems as well. When large numbers of graphs were required to be generated, shell scripts using the BASH environment were created to automate the creation of GNUPlot scripts, and in turn the required graphs for each data set. By generating graphs for all of the distributions across a selected section of road, the algorithm could be analysed for its eectiveness in allocating time-costs as discussed in section 3.3.4. These graphs allowed us to validate the values for V, t1 and t2 shown in 3.7 chosen for the velocity prole, as there were very few proles that followed the idealised velocity curve. Another useful use of GNUPlot was to plot interactive 3 dimensional graphs. By plotting velocity, distance and time on the x, y and z axes, we were able to visualise all the key variables for determining our models eectiveness for representing the data provided.

3.4.2

Probability Analysis

Once time costs were calculated for each trip, these were loaded into the database. Individual road or intersection costs were extracted for analysis from the database into OpenOce.orgs Spreadsheet tool. Standard probability graphs including cumulative distribution and frequency histograms were used to visualise the impact of dierent intersections.

3.4.3

Challenges

When the program to process our map data was rerun, the internal reference numbers for each segment of road was renumbered. This meant that it was cumbersome to compare results as all the information had to be correlated back to map data to provide a A specialist statistical package would have served better to analyse the statistical information that came from the model. Using the spreadsheet

23

package, while functional, was quite cumbersome and time consuming to produce results.

24

Chapter 4

Results
4.1 GPS Data

Over the course of the project, approximately one million data points were collected from the GPS data loggers placed in various cars. The data was spread as far west as Dubbo, and as far east as Newcastle. Other major centres that had data collected were Singleton, Sydney and Penrith. A map showing where data was collected from can be seen in gure 4.1. The highest concentrations of data were around Singleton. As such, the sample analysis work shown here was done using streets and intersections in this area.

Figure 4.1: GPS Data collected (red) shown with map data (black) and major cities (blue)

25

4.2

Road and Intersection Analysis

A segment of road along Bridgeman Road Singleton was taken to demonstrate analysis of the time cost associated with travelling along a section of road. The section starts at the intersection of Bridgeman Road and Blaxland Avenue, and continues south along Bridgeman Road until the intersection with the new England highway. This section of road is shown in gure 4.2

Figure 4.2: Section of road analysed, taken from Google Maps To obtain a basic view on the speed of a section of road, a frequency table of time-costs was constructed using bins two seconds wide. This then allowed for the construction of the graph shown in gure 4.3 which has been tted with a curve. From this graph it can easily be seen that the most common time-cost associated with travelling on this road was 38-40 seconds, with most trips being assigned a cost of between 36 and 48 seconds. Also on the graph we can see that no trips along this road took less than 32 seconds, and the longest time taken This relatively tight distribution of travel time along the road shows that the road itself is relatively unaected by external factors, and the road is unlikely to be a source of delays, with the intersections at either end of this segment are likely to be the source of delays. This nding is supported by observations made while collecting data. The road appeared to be free owing at all times of the day, irrespective of congestion and other factors. Another view of the same information is a cumulative distribution func26

16 14 12

Frequency

10 8 6 4 2 0 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62

Time (Seconds)

Figure 4.3: Time-cost Frequency Graph tion. While not as intuitive to read at rst, once understood, we can use this graph to understand the probability that we can travel along the road in a certain period of time, or in reverse, calculate the travel time for a particular condence interval. This is based on the assumption that the data is a statistically sound data-set.
100%

80%

60%

40%

20%

0% 35 40 45 50 55 60

Time Cost (Seconds)

Figure 4.4: Time-cost Cumulative Distribution Function By tracing a line up from 45 seconds, and looking at the value on the vertical axis at this point, we can determine that the probability of travelling along this section of road in 45 seconds or less is approximately 65%, equally, there is a 35% chance that a trip along this road will take more than 45 seconds. If we wish to travel along this road with 80% condence that we will arrive at a set time, we should allow approximately 47 seconds to travel 27

along this road. This second method can be used analytically to calculate the required time to travel along a route and is discussed further in section 4.3 Both the graphs in gures 4.3 and 4.4 can be converted to show the probability of an average speed along the section of road by dividing the distance of the road (approximately 800 metres), by the time-costs shown along the X-axis. In gure 4.5 is shown the paths that were travelled through the intersection of Bridgeman Road which runs north to south and the New England Highway running east to west. Each transition is shown in a separate colour, labelled with the from direction and the to direction, ie. N-E refers to the path traveling from the north, through the intersection and exiting to the east. Simply, the directions are points on the compass relative to the centre of the intersection.
200

175

150

Time Cost (Seconds)

125 SN NS NE NW EN WE

100

75

50

25

Sample Number

Figure 4.5: Time-cost at Intersection By Sample Number It can be clearly seen from the graph that the transition N-E is consistently fast, with a low time cost for every sample taken. This is explained by the fact that there is a slip-road in this direction, allowing the turning trac to avoid the trac lights and merge further east along the road. The next largest dataset obtained at this intersection is when travelling in the opposite direction, E-N, coming from the east, turning right across trac up to the North. The distribution of this transition can be explained by the lower values for time occurring during times of light trac such as at night, when the light sequence was favourable. The higher values are when the car either had to stop for lights or trac. Travelling from the north to turn west along the N-W path again shows

28

the impact of turning right across trac. The minimum time cost of this path is elevated from the E-N path as the major road, the New England Highway, is favoured causing the lights and trac to more frequently cause larger delays for this right turn across the ow of trac. The S-N path shows great variability in the time taken due to observation that the lights at the intersection favour the major road often blocking the S-N path, frequently causing delays in excess of 50 seconds. Other paths travelling straight through the intersection have too few samples to make any conclusive judgements regarding the intersection, however it would be expected that with greater volumes of data, it could be clearly shown that W-E/E-W paths would be quicker than the N-S/S-N paths. With increased data-volumes of data at this intersection, the inuence of trac and light timing could be better seen on the data and a more accurate picture of the time based inuences explored.

29

4.3

Sample Route Estimation

The data collected and the model built has the key objective of providing accurate trip time estimates. To do so, statistical data must be compiled for each road and intersection in the trip. This sort of task is best suited to an automated program, however to demonstrate how this would be done, it has been done here manually.

Figure 4.6: Sample route with roads numbered The rst thing that needs to be dened is the exact route that will be travelled. For the purposes of this example, a path from the shops in Singleton Heights to the main street, John St., in Singleton will be used. This path will go via the Newton St. bridge. This path is shown in gure 4.6 with the roads numbered. For this example, roads will be given a number, and the intersections between them will show the road numbers on either side of the intersection, e.g. 3-4 for the intersection from road 3 to road 4. The next step is to acquire the statistical data for each section of road and intersection. The statistics gathered for the roads and intersections can be seen in gures 4.7 and 4.8 respectively. To build the nal time estimations with probabilities, one could graphically combine the CDFs shown to arrive at an overall CDF for the route. As this would be quite error prone, an analytical method was used to combine

30

the probabilities and travel times of each road and intersection.


100% 90% 80% 70% 60% 50% 40% 30% 20% 10% 0% 0 10 20 30 40 50 60 70 80 90

Time Cost (Seconds)

Figure 4.7: CDF for road travel time in sample route

100% 90% 80% 70% 60% 50% 40% 30% 20% 10% 0% 0 20 40 60 80 100 120 140 160 180 200

Time Cost (Seconds)

Figure 4.8: CDF for intersection travel time in sample route To analytically combine the CDFs, set steps for the probability must be used. In this case, steps of 10% were used. Intervals of 10% for each road and intersection were then calculated, using linear interpolation of the available data at these points to arrive with a nal set of data for roads and intersections. These values were then tabulated, as shown in table 4.1 and the values at each probability interval summed to arrive at the set intervals.

31

1 10% 20% 30% 40% 50% 60% 70% 80% 90% 100% 29.8 30.9 31.3 31.7 32.7 33.6 34.7 35.5 36.4 50.2 4-5 10% 20% 30% 40% 50% 60% 70% 80% 90% 100% 5.8 5.9 7.1 7.7 7.9 8.2 8.9 10.1 19.9 23.5

1-2 5.6 6.5 7.0 7.8 8.4 9.7 11.1 14.9 20.8 42.4 5 7.4 7.5 8.1 8.3 8.4 8.6 9.1 9.3 10.4 12.9

2 38.2 39.3 40.6 41.6 43.0 44.6 45.4 46.7 48.1 59.1 5-6 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.2 0.3 1.7

2-3 7.0 8.4 10.3 11.4 17.1 22.8 45.1 61.5 102.9 197.2 6 16.8 18.0 18.9 19.3 20.4 21.7 21.9 23.6 24.5 26.1

3 18.3 19.9 20.9 21.5 21.8 22.3 23.1 24.5 25.3 31.0 6-7 0.0 0.3 1.0 1.6 7.3 11.1 15.1 16.8 28.7 42.2

3-4 4.7 4.8 5.4 5.6 5.9 6.5 7.6 11.9 13.6 17.9 7 22.9 25.8 27.3 27.9 28.5 29.9 33.3 42.6 45.0 89.4

4 16.7 18.1 18.3 18.6 18.8 18.9 21.0 21.7 22.6 23.1 173.2 185.4 196.1 203.0 220.1 237.9 276.3 319.3 398.4 616.6

Table 4.1: Interpolated CDF points along sample route

32

Graphing the aggregated values provides us with an overall view of the time taken to travel along this route and a number of key observations can be made using this data. The graph of this data can be seen in gure 4.9.
100% 90% 80% 70% 60% 50% 40% 30% 20% 10% 0% 170 245 320 395 470 545 620

Time Cost (Seconds)

Figure 4.9: CDF for Sample Route travel time It can be said that on average, that is, with a 50/50, chance that this trip will take 220 seconds. By considering the slope of the graph, or alternatively the dierence in the nal values in table 4.1, we can see that each 10% increase in condence interval has an increasingly large time cost. If we want to plan for a higher level of condence, say 90%, we would allow for the trip to take upto 400 seconds, excluding time taken to embark and disembark on this trip. Based on our data, we should nd that no matter what the case, 620 seconds will allow us to make this trip. In reality however, one could never have a 100% condence interval when estimating trip times as all manner of catastrophic events such as oods, as has happened along this route in the past. Care and sound statistical methods must be employed when drawing conclusions from the data provided as an output of this system. We can see that the system developed is able to generate realistic and reasonable statistics that can be used as the foundation of time estimation.

33

4.4

Speeds and Time of day

One key outcome that was anticipated was to validate the inuence of time of day on trip time. A graph was generated based on our sample area to show the correlation. Individual time costs assigned to a piece of road were compared against the average time cost of the road with respect to the time at which they originally occurred. By grouping information in 15 minute intervals, the graph in gure 4.10 was generated. we can see that on average, the average time only varies slightly. This may be a due to the limited data associated with these roads or a that samples were not obtained in both favourable and unfavourable trac conditions. Four key points of interest, labelled POI were identied on the graph.
200% POI Zero Min Avg Max

150%

Difference from Average Cost

100%

50%

0%

50%

100% 7 9 11 13 15 17 19 21 23

Hour of Day

Figure 4.10: Time trending of variation in travel time The rst key point on the graph is for the period 7:30-7:45am, corresponding with a peak time on Singleton roads for sta members of local business who start at 8:00am. There likely would have been a peak sometime before 7:00am due to the shift start times of the local mines, however no data was collected at this time. The point at 2:15pm appears to be an anomaly in the data set as no justication for this increased maximum time could be identied. This may have been due to road-works or similar unplanned delay. There is only one data-point collected at this time in a set of 8 that has such a large magnitude. If we exclude this maximum point, of a +156% delay, the next largest delay at this time period is +18%. Peak hour in the afternoon is visibly captured during the period 5:305:45pm and the maximum and average delays return to normal conditions 34

over the next half an hour. While there is not the severe peak of the rst two points of interest, the maximum travel time increase of 62% and an increase in average travel time of 17%. The last point of interest on the graph is during the period 7:45-8:00pm. As the shifts of some local mines end at 7pm, this trac may be a result of that, however with the high degree of variability in the numbers, more data would need to be collected to be able to draw any statistically valid conclusions.

35

Chapter 5

Conclusions
5.1 Project Summary

Creating a robust methodology to automatically generate a statistical model of public roads proved to be exceedingly more time consuming than originally estimated. The complexities of developing such a system are underlined by the fact that no commercial system currently exists, taking this detailed approach to provide understanding of trac ow. While limited in scope due to time and resources, this project has achieved its aim of developing a methodology and basic infrastructure for the automated generation of a statistical model for a given area of a road network. To better develop this system, data from additional drivers who over-lapped would have beneted the evolution of this system greatly. As it was, there was limited data available, and very limited over-lap of data in the system. An unintended, but logical outcome of this project is the greater awareness of issues relating to trac ow that were encountered during the course of the project. This increased awareness only serves to assist in making more optimal decisions when travelling.

5.2

Future Directions

With limited renement of the core algorithm used and automation of data collection and storage, it is quite feasible to propose that a fully automated system could be implemented. This automated system could then be used to supplement and help validate the time estimates used in mobile GPS devices and mapping applications. Several key questions remain unanswered by this project due to the limitations of time, resources and scope. Firstly, How much of a role does the driver play in reducing travel times? Without comparative data between drivers, along the same roads, at the same time of day, there is no objective base for comparison. How much 36

inuence does a driver actually exert over their travel times while obeying the road rules? What costs do the dynamic and chaotic nature of road networks impose on travel times. Another key question raised is to what degree do the unaddressed inuences outlined in section 3.3.6 aect the travel times of road users? Do the delays typically experienced because of these inuences justify the expense of more advanced and costly methods to identify, combat or avoid the delays. What is the probabilistic distribution of these individual inuences? Which inuences aect the most people, most often? If we are to have a system to gather and process this information, how can it best be integrated with existing approaches to time estimates and route optimisation that provides the most benets to the road users. With more time and resources to develop an automated system and much larger volumes of data collected over time, answers to these questions could not only be obtained in general terms, but be quantied with a degree of accuracy ever increasing as the volume of data in the system increases.

37

Appendix A

Processor Source Code


This appendix contains the C++ source code used to process the raw points, map it to the map data and provide an output with which to build a statisical model as discussed in section 3.3.

38

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: #include <iostream> #include <sstream> #include <iomanip> #include "Database.h" using namespace std; Database::Database() { con = NULL; connected = false; } Database::Database() { //dtor } bool Database::connect() { try { con = new Connection(DB_NAME, DB_HOST, DB_USER, DB_PASS); } catch (BadQuery er) { // Die if we couldnt connect cerr << "Error connecting to database : " << endl << \t << er.what() << endl; connected = false; return false; }

Database.cpp, p1/3

// Connection failed to allocate memory if (!con) { cerr << "Connection creation failed. Out of memory?" << endl; connected = false; return false; } cout << "Connection established" << endl; connected = true; return true; } void Database::disconnect() { try { con->disconnect(); } catch(Exception e) {} } void Database::addRoad(sql_bigint_unsigned model, sql_bigint_unsigned way, sql_bigint_unsigned nodeFrom, sql_bigint_unsigned nodeTo, sql_bigint_unsigned seqFrom, sql_bigint_unsigned seqTo) { Query query = con->query(); query << "INSERT INTO model_road SET " << "model_id=" << model << ",way_id=" << way << ",node_from=" << nodeFrom << ",node_to=" << nodeTo << ",sequence_from=" << seqFrom << ",sequence_to=" << seqTo; try{ SimpleResult res = query.execute(); } catch (BadQuery) { } }

Appendix A - Source Code 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: void Database::deleteRoads(int model) { Query query = con->query(); query << "DELETE FROM model_road WHERE " << "model_id=" << model; try { SimpleResult res = query.execute(); } catch(BadQuery) { } } void Database::deletePoints(int model) { Query query = con->query(); query << "DELETE FROM model_point_list " << "WHERE model_id=" << model; try { SimpleResult res = query.execute(); } catch(BadQuery) { } }

Database.cpp, p2/3

bool Database::addPoint( int model, sql_bigint_unsigned point, sql_bigint_unsigned road, double heading) { Query query = con->query(); query << << << << << "INSERT INTO model_point_list SET " "road_id=" << road ", heading=" << heading ", model_id=" << model ", point_id=" << point;

string s = query.str(); try { SimpleResult res = query.execute(); } catch(BadQuery er) { cout << "FAIL: " << er.what() << endl << \t << s; return false; } return true; } bool Database::updateMapNode(sql_bigint_unsigned node, Point2D northingEasting) { stringstream sn, se; sn << fixed << northingEasting.y; se << fixed << northingEasting.x; Query query = con->query(); query << "UPDATE map_node SET " << " northing=" << sn.str() << ", easting=" << se.str() << " WHERE id=" << node; string s = query.str(); try { SimpleResult res = query.execute(); } catch(BadQuery er) { cout << "FAIL: " << er.what() << endl << \t << s; return false; } return true;

Appendix A - Source Code 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: }

Database.cpp, p3/3

bool Database::updateRawPoint(sql_bigint_unsigned point, Point2D northingEasting) { stringstream sn, se; sn << fixed << northingEasting.y; se << fixed << northingEasting.x; Query query = con->query(); query << "UPDATE raw_point SET " << " northing=" << sn.str() << ", easting=" << se.str() << " WHERE id=" << point; string s = query.str(); try { SimpleResult res = query.execute(); } catch(BadQuery er) { cout << "FAIL: " << er.what() << endl << \t << s; return false; } return true; } void Database::startTransaction() { trans = new Transaction(*con); } void Database::commit() { trans->commit(); delete trans; }

Appendix A - Source Code

Data.cpp, p1/1

1: #include "Data.h" 2: 3: #include <iostream> 4: 5: Data* Data::me = NULL; 6: 7: Data::Data() { 8: db = NULL; 9: } 10: 11: Data::Data() { } 12: 13: Data* Data::ptr() { 14: if (me == NULL) { 15: me = new Data(); 16: } 17: 18: return me; 19: } 20: 21: Data& Data::ref() { 22: if (me == NULL) { 23: me = new Data(); 24: } 25: 26: return *me; 27: } 28: 29: void Data::getConnection() { 30: if (db) return; 31: 32: db = new Database(); 33: db->connect(); 34: } 35: 36: double Data::getRoadLength(long id) { 37: map<long, double>::const_iterator it = roadLength.find(id); 38: if (it != roadLength.end()) return (*it).second; 39: 40: getConnection(); 41: 42: Query qry = db->getConnection()->query(); 43: qry << "SELECT COALESCE(SUM(dist),0) as sumDist FROM vw_road_segments WHERE road_id=" << id; 44: 45: try { 46: StoreQueryResult res = qry.store(); 47: 48: if (res) { 49: double dist = res.num_rows() ? res[0]["sumDist"] : 0.0; 50: roadLength[id] = dist; 51: return dist; 52: } 53: 54: } catch (const Exception& er) { 55: cout << endl << "!!! ERROR:" << er.what() << endl 56: << \t << qry.str() << endl; 57: } 58: 59: roadLength[id] = 0.0; 60: return 0.0; 61: }

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: #include #include #include #include #include "Intersection.h" <sstream> <iostream> <fstream> <map>

Intersection.cpp, p1/2

using namespace std; Intersection::Intersection(unsigned int id) { this->id = id; // currentTrace = NULL; } Intersection::Intersection() { // Road* r; // while(!roads.empty()) { // r = (*(roads.begin())).second; // roads.erase(roads.begin()); // } } void Intersection::addRoad(Road* r) { if (!r) return; if (findRoad(r->getID())) return; roads[r->getID()] = r; } Road* Intersection::findRoad(unsigned int id) { map<unsigned int, Road*>::const_iterator it = roads.find(id); if (it == roads.end()) return NULL; return (*it).second; } // //void Intersection::newTrace() { // if (currentTrace) { // //If we dont get close enough, nuke the trace // if ((currentTrace->getMinDist() > 100.0) || // (currentTrace->getPointCount() < 10)){ // traces.pop_back(); // delete currentTrace; // currentTrace = NULL; // } // } // // currentTrace = new Trace(); // traces.push_back(currentTrace); //} // //void Intersection::add(TDSTuple tuple) { // if (!currentTrace) newTrace(); // // currentTrace->add(tuple); //} // //void Intersection::dumpTraces() { // int i = 0; // // list<Trace*>::iterator it; // list<TDSTuple>::iterator it2; // Trace* trace; // // cout << traces.size() << " traces. " << endl; // for(it = traces.begin(); it != traces.end(); it++) { // cout << "*** " << ++i << "] "; // trace = (*it); // for (it2 = trace->tds.begin(); it2 != trace->tds.end(); it2++) {

Appendix A - Source Code 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136:

Intersection.cpp, p2/2

// TDSTuple& tds = (*it2); // cout << ( << tds.time << ") "; // } // cout << endl; // } //} // //void Intersection::saveTraces() { // stringstream ss; // ss << "intersection_" << id << ".dat"; // // ofstream f; // f.open(ss.str().c_str()); // // list<Trace*>::iterator it; // Trace* t; // int i=0; // for(it = traces.begin(); it != traces.end(); it++) { // t = (*it); // t->calculate(); // // ss.clear(); ss.str(""); // ss << "i" << id << "_t" << i; // if (t->isBad()) { // ss << "bad"; // } // ss << ".dat"; // ofstream tf; // tf.open(ss.str().c_str()); // // f << *t << endl; // tf << *t << endl; // i++; // tf.close(); // } // // f.close(); //} // //void Intersection::buildTable() { // pair<unsigned long, unsigned long> p; // map<pair<unsigned long, unsigned long>, list<double> > timetable; // map<pair<unsigned long, unsigned long>, list<double> >::iterator ttit; // list<Trace*>::iterator it; // list<double>::iterator dit; // Trace* t; // for(it = traces.begin(); it != traces.end(); it++) { // t = (*it); // p.first = t->getRoadIn(); // p.second = t->getRoadOut(); // if (p.first && p.second) { // timetable[p].push_back(t->getDelay()); // } // } // // cout << "Timetable:" << endl; // for (ttit = timetable.begin(); ttit != timetable.end(); ttit++) { // p = (*ttit).first; // cout << p.first << ", " << p.second << :; // list<double> & tempList = (*ttit).second; // tempList.sort(); // for (dit = tempList.begin(); dit != tempList.end(); dit++) { // cout << *dit << ; // } // cout << endl; // } //}

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: #include <iostream> #include <math.h> #include "ModelGenerator.h" #include "Toolbox.h" #include "Projection.h" using namespace std; void displayPoint(Point2D p, const char* name) { cout << name << ( << p.x << , << p.y << ") "; } int main() { ModelGenerator* gen = new ModelGenerator(); gen->build(1); }

main.cpp, p1/1

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: #include "Model.h" #include <iostream> #include <fstream> using namespace std; Model::Model(int id) { //ctor this->id = id; } Model::Model() { //dtor }

Model.cpp, p1/2

void Model::dumpSegments() { list<Segment*>::iterator itSeg; Segment* s; //int lastRoad = 0; for(itSeg = segments.begin(); itSeg != segments.end(); itSeg++) { s = (*itSeg); cout << fixed << s->road << << s->segment << << s->heading << << s->length << << s->start.x << << s->start.y << << s->stop.x << << s->stop.y << endl; } } void Model::writePoints() { ofstream f("all_points"); ofstream uf("un_points"); ofstream af("as_points"); list<Point*>::iterator it; Point* p; for (it = points.begin(); it != points.end(); it++) { p = (*it); f << fixed << p->coords.x << << p->coords.y << endl; if (p->nearRoad) { af << fixed << p->coords.x << << p->coords.y << endl; } else { uf << fixed << p->coords.x << << p->coords.y << endl; } } f.close(); af.close(); uf.close(); } void Model::writeSegments() { ofstream f("segments"); list<Segment*>::iterator itSeg; Segment* s; Segment* lastSeg = NULL; unsigned int lastRoad = 0; for(itSeg = segments.begin(); itSeg != segments.end(); itSeg++) { s = (*itSeg); if (lastRoad && s->road != lastRoad) { f << lastSeg->stop.x << << lastSeg->stop.y << endl << endl; } f << fixed << s->start.x << << s->start.y << endl; lastRoad = s->road; lastSeg = s; }

Appendix A - Source Code 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: } void Model::writeTrips() { ofstream f; f.open("trips"); cout << "Writing trips: "; list<Trip*>::iterator it; Trip* t; int i=0; for(it = trips.begin(); it != trips.end(); it++) { t = (*it); t->mapToIntersections(this); t->checkPath(); t->walkParts(); } for(it = trips.begin(); it != trips.end(); it++) { t = (*it); t->calcCosts(); t->write(i++); f << *t << endl << endl; } cout << " done!" << endl; f.close();

Model.cpp, p2/2

ofstream all; all.open("all_intersections.dat"); map<unsigned long, Point*>::const_iterator iit; Point* p; for(iit = interPoints.begin(); iit != interPoints.end(); iit++) { p = (*iit).second; all << p->id << << p->coords.x << << p->coords.y << endl; } all.close(); } Road* Model::findRoad(unsigned int id) { map<unsigned int, Road*>::const_iterator it = roads.find(id); if (it == roads.end()) return NULL; return (*it).second; } Point* Model::findIterPoint(unsigned long id) { map<unsigned long, Point*>::const_iterator it = interPoints.find(id); if (it == interPoints.end()) return NULL; return (*it).second; } void Model::add(Intersection* i) { if (i == NULL) return; intersections[i->getID()] = i; } void Model::add(Trip* t) { trips.push_back(t); }

void Model::addCorrection(unsigned long intersectionID, Point2D offset) { corrections.push_back(pair<unsigned long, Point2D>(intersectionID, offset)); }

Appendix A - Source Code

ModelGenerator.cpp, p1/12

1: #include "ModelGenerator.h" 2: 3: #include <mysql++/mysql++.h> 4: #include <fstream> 5: 6: #ifndef NULL 7: #define NULL 0 8: #endif 9: 10: #include "Intersection.h" 11: #include "Toolbox.h" 12: #include "Projection.h" 13: #include "Segment.h" 14: #include "Point.h" 15: #include "TDSTuple.h" 16: #include "Stats.h" 17: 18: using namespace mysqlpp; 19: using namespace std; 20: 21: ModelGenerator::ModelGenerator() { 22: db = NULL; 23: } 24: 25: ModelGenerator::ModelGenerator() { 26: //dtor 27: } 28: 29: void ModelGenerator::build(int modelID) { 30: db = new Database(); 31: db->connect(); 32: if (!db->isConnected()) { 33: delete db; 34: return; 35: } 36: 37: currentModel = new Model(modelID); 38: 39: assignNorthingEasting(); 40: 41: buildRoads(); 42: loadSegments(); 43: loadPoints(); 44: matchPoints(); 45: checkPoints(); 46: savePoints(); 47: 48: makeTrips(); 49: 50: processIntersections(); 51: 52: currentModel->writeTrips(); 53: 54: Stats::ref().dump(); 55: Stats::ref().writeToDB(modelID); 56: 57: currentModel->dumpSegments(); 58: currentModel->writePoints(); 59: currentModel->writeSegments(); 60: } 61: 62: void ModelGenerator::buildRoads() { 63: db->deleteRoads(currentModel->getID()); 64: 65: Query qry = db->getConnection()->query(); 66: qry << "SELECT " 67: << " mwl.model_id, mwl.way_id, wp.node_id, wp.sequence, COALESCE(id, 0) AS intersection "

Appendix A - Source Code

ModelGenerator.cpp, p2/12

68: << "FROM model_way_list mwl " 69: << "LEFT JOIN map_way_point wp ON mwl.way_id = wp.way_id " 70: << "LEFT JOIN model_intersection i ON wp.node_id = i.node_id " 71: << "WHERE " 72: << " mwl.model_id = " << currentModel->getID() << " AND " 73: << " ((i.id IS NOT null) OR " 74: << " wp.sequence = 0 OR " 75: << " wp.sequence = " 76: << " (SELECT MAX(sequence) " 77: << " FROM map_way_point " 78: << " WHERE way_id = wp.way_id)) " 79: << "ORDER BY way_id, sequence "; 80: 81: try { 82: StoreQueryResult res = qry.store(); 83: sql_bigint_unsigned way, lastWay; 84: sql_bigint_unsigned node, lastPoint; 85: sql_bigint_unsigned sequence, lastSequence; 86: 87: way = lastWay = 0; 88: node = lastPoint = 0; 89: sequence = lastSequence = 0; 90: 91: cout << "Creating Roads:"; 92: 93: if (res) { 94: for(size_t i=0; i < res.num_rows(); ++i) { 95: way = res[i]["way_id"]; 96: node = res[i]["node_id"]; 97: sequence = res[i]["sequence"]; 98: 99: if (lastWay == way) { 100: db->addRoad(currentModel->getID(), way, lastPoint, node, 101: lastSequence, sequence); 102: cout << .; 103: } 104: 105: lastWay = way; 106: lastPoint = node; 107: lastSequence = sequence; 108: 109: } 110: 111: cout << endl; 112: } 113: } catch (exception e) { 114: 115: } 116: } 117: 118: void ModelGenerator::loadSegments() { 119: Query querySeg = db->getConnection()->query(); 120: querySeg << "SELECT * FROM vw_road_segments " 121: << "WHERE model_id=" << currentModel->getID(); 122: 123: list<Segment*>& segments = currentModel->getSegments(); 124: Segment* s; 125: 126: try { 127: cout << "*** Loading segments ... "; 128: StoreQueryResult res = querySeg.store(); 129: 130: if (res) { 131: for(size_t i=0; i < res.num_rows(); ++i) { 132: s = new Segment(); 133: s->road = res[i]["road_id"]; 134: s->segment = res[i]["segment"]; 135: s->start.x = res[i]["n1_easting"];

Appendix A - Source Code

ModelGenerator.cpp, p3/12

136: s->start.y = res[i]["n1_northing"]; 137: s->stop.x = res[i]["n2_easting"]; 138: s->stop.y = res[i]["n2_northing"]; 139: s->heading = Toolbox::calculateDirection(s->start, s->stop); 140: s->length = Toolbox::calculateDistance(s->start, s->stop); 141: 142: segments.push_back(s); 143: } 144: } 145: cout << res.num_rows() << " segments loaded." << endl; 146: 147: } catch (const Exception& er) { 148: cout <<endl << "!!! ERROR:" << er.what(); 149: } 150: } 151: 152: void ModelGenerator::loadPoints() { 153: Query queryPoint = db->getConnection()->query(); 154: queryPoint << "SELECT mpl.point_id, rp.easting, rp.northing, rp.speed, " 155: << " UNIX_TIMESTAMP(rp.point_time) as ts, rp.dataset_id " 156: << "FROM model_point_list mpl " 157: << "LEFT JOIN raw_point rp ON (mpl.point_id = rp.id) " 158: << "WHERE mpl.model_id=" << currentModel->getID() 159: << " AND (rp.lat IS NOT NULL) AND (rp.easting IS NOT NULL) " 160: << " AND rp.speed > 0.1 " 161: << "ORDER BY rp.dataset_id, rp.point_time"; 162: 163: //<< " LIMIT 10"; 164: cout << "SQL: " << queryPoint.str() << endl; 165: 166: list<Point*>& points = currentModel->getPoints(); 167: Point* n, *lastPoint; 168: double tmp; 169: 170: try { 171: cout << "*** Loading points "; 172: StoreQueryResult res2 = queryPoint.store(); 173: 174: if (res2) { 175: lastPoint = NULL; 176: for(size_t i=0; i < res2.num_rows(); ++i) { 177: n = new Point(); 178: n->id = res2[i]["point_id"]; 179: n->coords.x = res2[i]["easting"]; 180: n->coords.y = res2[i]["northing"]; 181: n->time = res2[i]["ts"]; 182: n->dataset = res2[i]["dataset_id"]; 183: n->speed = res2[i]["speed"]; 184: 185: if (lastPoint) { 186: if ((n->time - lastPoint->time) > 900) { 187: n->heading = -1.0; //too much time has passed, cant trust it. 188: } else { 189: n->heading = Toolbox::calculateDirection(lastPoint->coords, n->coords); 190: tmp = Toolbox::minDOrient(n->orientation(),lastPoint->orientation()); 191: 192: // Correct low-speed jitter 193: if ((n->speed < 4.0) && (tmp > 15.0)) { 194: n->heading = lastPoint->heading; 195: } 196: 197: } 198: } else { 199: n->heading = -1.0; 200: }

Appendix A - Source Code

ModelGenerator.cpp, p4/12

201: 202: points.push_back(n); 203: 204: if ((i % 1000) == 0)cout << "."; 205: lastPoint = n; 206: } 207: } 208: cout << << res2.num_rows() << " points loaded. " << endl; 209: } catch (const Exception& er) { 210: cout << endl << "!!! ERROR:" << er.what() << endl 211: << \t << queryPoint.str() << endl; 212: } 213: 214: cout << "Check point size:" << points.size() << "points" << endl; 215: 216: } 217: 218: void ModelGenerator::matchPoints() { 219: double maxDist = 30.0; 220: double orient = 30.0; // 10degree margin of error for ways 221: 222: int i = 1; 223: double dOrient; 224: 225: list<Segment*>& segments = currentModel->getSegments(); 226: list<Point*>& points = currentModel->getPoints(); 227: 228: cout << "Segments: " << segments.size() << "sg, " 229: << points.size() << "points" << endl; 230: 231: list<Segment*>::iterator itSeg; 232: list<Point*>::iterator itPoint; 233: 234: Point* curPoint; 235: Segment* curSeg; 236: double dist; 237: 238: int max = points.size(); 239: int increment = max / 10; 240: int assigned = 0; 241: 242: cout << "Matching points to path: 0% "; 243: for(itPoint = points.begin(); itPoint != points.end(); itPoint++) { 244: // for (i=1; i<5; i++) { 245: curPoint = (*itPoint); 246: for(itSeg = segments.begin(); itSeg != segments.end(); itSeg++) { 247: curSeg = (*itSeg); 248: dist = curSeg->distanceTo((*itPoint)->coords); 249: dOrient = Toolbox::minDOrient(curPoint->orientation(), curSeg->orientation()); 250: 251: // within distance, nearest so far, orientated roughly correctly 252: if (dist <= maxDist && 253: (!curPoint->nearRoad || curPoint->nearDistance > dist) && 254: ((dOrient < orient) || curSeg->length < 10.0 )) { 255: curPoint->nearDistance = dist; 256: curPoint->nearRoad = curSeg->road; 257: curPoint->nearSegment = curSeg->segment; 258: } 259: } 260: 261: if (i%increment == 0) { 262: cout << "..." << (i*100) / max << %; 263: cout.flush(); 264: } 265: 266: if (curPoint->nearRoad) assigned++; 267:

Appendix A - Source Code

ModelGenerator.cpp, p5/12

268: i++; 269: } 270: 271: cout << " Assigned " << (assigned * 100) / max << "%"; 272: cout << endl; 273: } 274: 275: void ModelGenerator::checkPoints() { 276: // return; 277: list<Point*>& points = currentModel->getPoints(); 278: list<Point*>::iterator itPoint; 279: 280: int hitA, hitB, hitC; 281: hitA = hitB = hitC = 0; 282: 283: int sampleSize = 5; 284: Point** p = new Point*[5]; 285: sql_bigint_unsigned nr[5]; 286: bool ok = false; 287: 288: for (int i=0; i < sampleSize; i++) { 289: p[i] = NULL; 290: } 291: 292: cout << "Check for point errors ... "; 293: 294: for(itPoint = points.begin(); itPoint != points.end(); itPoint++) { 295: p[0] = (*itPoint); 296: ok = true; 297: 298: for(int i=0; i < sampleSize; i++ ) { 299: if (!p[i]) { 300: ok = false; 301: nr[i] = 0; 302: } else { 303: nr[i] = p[i]->nearRoad; 304: } 305: } 306: 307: ok = ok; 308: 309: //If our second point is empty but the first and third are the same use 310: // the first details 311: if (p[0] && p[1] && p[2] && !p[1]->nearRoad && p[0]->nearRoad && 312: (nr[0]==nr[2])) { 313: p[1]->nearRoad = p[0]->nearRoad; 314: p[1]->nearSegment = p[0]->nearSegment; 315: hitA++; 316: // If the first two points are the same, and the last two points are the 317: // same, and the middle point is unique set it to the same as the first 318: // two 319: } else if (ok && (p[0]->nearRoad == p[1]->nearRoad) && 320: (p[3]->nearRoad == p[4]->nearRoad) && 321: (p[2]->nearRoad != p[1]->nearRoad) && 322: (p[2]->nearRoad != p[3]->nearRoad) ){ 323: p[2]->nearRoad = p[0]->nearRoad; 324: p[2]->nearSegment = p[0]->nearSegment; 325: hitB++; 326: } else if (ok && (p[0]->nearRoad == p[4]->nearRoad) && 327: (p[1]->nearRoad == p[0]->nearRoad) && 328: (p[2]->nearRoad != p[0]->nearRoad) && 329: (p[3]->nearRoad != p[0]->nearRoad)) { 330: p[2]->nearRoad = p[0]->nearRoad; 331: p[2]->nearSegment = p[0]->nearSegment; 332: p[3]->nearRoad = p[0]->nearRoad; 333: p[3]->nearSegment = p[0]->nearSegment; 334: hitC++; 335: }

Appendix A - Source Code 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403:

ModelGenerator.cpp, p6/12

//Shuffle for (int i=1; i < sampleSize; i++) { p[sampleSize-i] = p[sampleSize-i-1]; } } Trip* t; for(list<Trip*>::const_iterator it = currentModel->getTrips().begin(); it != currentModel->getTrips().end(); it++) { t = (*it); for(map<int,Point*>::const_iterator it2 = t->points.begin(); it2 != t->points.end(); it2++) {

} } cout << "fixed " << (hitA+hitB+hitC) << " errors (" << hitA << , << hitB << , << hitC << ")" << endl; } void ModelGenerator::savePoints() { db->deletePoints(currentModel->getID()); list<Point*>& points = currentModel->getPoints(); list<Point*>::iterator itPoint; Point* curPoint; int i = 1; int max = points.size(); int increment = max / 10; cout << "Saving points: 0%"; for(itPoint = points.begin(); itPoint != points.end(); itPoint++) { curPoint = (*itPoint); db->addPoint(currentModel->getID(), curPoint->id, curPoint->nearRoad, curPoint->heading); if (i%increment == 0) { cout << "..." << (i*100) / max << %; cout.flush(); } i++; } cout << endl; } void ModelGenerator::assignNorthingEasting() { //Avoid massive query results, do it a bit at a time. int value=1; cout << "Calculating NEs from LLs: "; while (value > 0) { cout << "."; cout.flush(); value = assignNorthingEastingNodes(10000); } value = 1; while (value > 0) { cout << ","; cout.flush(); value = assignNorthingEastingPoints(10000); } cout << endl;

Appendix A - Source Code 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: }

ModelGenerator.cpp, p7/12

int ModelGenerator::assignNorthingEastingNodes(int limit) { Query query = db->getConnection()->query(); query << "SELECT * FROM map_node WHERE northing=0 OR easting=0" << " LIMIT " << limit; try { StoreQueryResult res = query.store(); if (res) { for(size_t i=0; i < res.num_rows(); ++i) { db->updateMapNode( res[i]["id"], Projection::convertLLtoNE( Point2D(res[i]["lon"],res[i]["lat"]))); } } return res.num_rows(); } catch (const Exception& er) { cout << endl << "!!! ERROR:" << er.what() << endl << \t << query.str() << endl; } return 0; } int ModelGenerator::assignNorthingEastingPoints(int limit) { Query query = db->getConnection()->query(); query << "SELECT * FROM raw_point WHERE northing=0 OR easting=0" << " LIMIT " << limit; try { StoreQueryResult res = query.store(); if (res) { for(size_t i=0; i < res.num_rows(); ++i) { db->updateRawPoint( res[i]["id"], Projection::convertLLtoNE( Point2D(res[i]["lon"],res[i]["lat"]))); } } return res.num_rows(); } catch (const Exception& er) { cout << endl << "!!! ERROR:" << er.what() << endl << \t << query.str() << endl; } return 0; } void ModelGenerator::makeTrips() { list<Point*>& points = currentModel->getPoints(); list<Point*>::iterator itPoint; Point* curPoint, *lastPoint = NULL; size_t max = points.size(); int i = 0; int increment = max / 10; Trip* t = new Trip(currentModel); cout << "Making trips: ";

Appendix A - Source Code

ModelGenerator.cpp, p8/12

472: for(itPoint = points.begin(); itPoint != points.end(); itPoint++) { 473: curPoint = (*itPoint); 474: if (i%increment == 0) { 475: cout << "..." << (i*100) / max << %; 476: cout.flush(); 477: } 478: 479: if (lastPoint) { 480: //fix time wierdness ... unsigned values, be careful! 481: if ( (lastPoint->dataset == curPoint->dataset) && 482: (curPoint->time - lastPoint->time ) > 2*lastPoint->time) { 483: //dodgy data 484: } else if ((lastPoint->dataset != curPoint->dataset) || 485: ((curPoint->time - lastPoint->time) > 1200) ) { 486: //cout << "+" << *lastPoint << " -> " << *curPoint << endl; 487: if (t->points.size() > 40) { 488: currentModel->add(t); 489: } else { 490: delete t; 491: } 492: t = new Trip(currentModel); 493: } 494: } 495: 496: t->add(curPoint); 497: 498: lastPoint = curPoint; 499: i++; 500: } 501: 502: currentModel->add(t); 503: 504: cout << " ... " << currentModel->getTrips().size() << " trips." << endl; 505: } 506: 507: void ModelGenerator::processIntersections() { 508: Query query = db->getConnection()->query(); 509: 510: query << "SELECT i.node_id, n.northing, n.easting " 511: << "FROM model_intersection i " 512: << "LEFT JOIN map_node n ON i.node_id = n.id " 513: << "WHERE i.model_id=" << currentModel->getID(); 514: 515: unsigned long intersectionID = 0; 516: list<unsigned long> intersections; 517: 518: Point* p; 519: 520: //Get a list of intersections to use 521: try { 522: if (UseQueryResult res = query.use()) { 523: while (mysqlpp::Row row = res.fetch_row()) { 524: intersectionID = row["node_id"]; 525: intersections.push_back(intersectionID); 526: p = new Point(intersectionID, Point2D(row["easting"], row["northing" ])); 527: currentModel->interPoints[intersectionID] = p; 528: } 529: } 530: } catch (const BadQuery& er) { 531: cout << endl << "!!! ERROR:" << er.what() << endl 532: << \t << query.str() << endl; 533: } 534: 535: //Process them one-by-one 536: list<unsigned long>::iterator it; 537: for(it = intersections.begin(); it != intersections.end(); it++) { 538: //processIntersection(*it);

Appendix A - Source Code

ModelGenerator.cpp, p9/12

539: guessIntersection(*it); 540: } 541: 542: cout << "Intersection corrections: " << currentModel->getCorrections().size() << endl; 543: } 544: 545: void ModelGenerator::processIntersection(unsigned long id) { 546: Query query = db->getConnection()->query(); 547: query << "SELECT " 548: << " r.id as road_id, " 549: << " SQRT((n.easting-rp.easting)*(n.easting-rp.easting)+" 550: << " (n.northing-rp.northing)*(n.northing-rp.northing)) as dist," 551: << " p.heading, " 552: << " rp.speed, " 553: << " rp.dataset_id, " 554: << " UNIX_TIMESTAMP(rp.point_time) as ts " 555: << "FROM model_intersection i " 556: << "LEFT JOIN map_node n ON (i.node_id = n.id) " 557: << "LEFT JOIN model_road r ON (i.model_id=r.model_id AND " 558: << "(i.node_id = r.node_from or i.node_id=r.node_to) ) " 559: << "LEFT JOIN model_point_list p ON (r.id = p.road_id) " 560: << "LEFT JOIN raw_point rp ON (p.point_id = rp.id) " 561: << "WHERE i.model_id=" << currentModel->getID() 562: << " AND i.node_id = " << id 563: << " AND rp.dataset_id IS NOT NULL " 564: << " ORDER BY rp.dataset_id, rp.point_time"; 565: 566: cout << "Process Intersection #" << id << endl; 567: // << \t << query.str() << endl; 568: 569: Intersection* inter = new Intersection(id); 570: Road* r = NULL; 571: 572: sql_bigint_unsigned lastDataset, dataset, lastTimestamp, timestamp, dTime, 573: road; 574: 575: double distance, lastDistance, speed, heading, dDist, calcSpeed; 576: 577: lastDataset = lastTimestamp = 0; 578: 579: list<TDSTuple> distanceSpeed; 580: 581: try { 582: if (UseQueryResult res = query.use()) { 583: while (mysqlpp::Row row = res.fetch_row()) { 584: road = row["road_id"]; 585: r = currentModel->findRoad(road); 586: if (!r) { 587: r = new Road(road); 588: inter->addRoad(r); 589: } 590: 591: distance = row["dist"]; 592: heading = row["heading"]; 593: speed = row["speed"]; 594: dataset = row["dataset_id"]; 595: timestamp = row["ts"]; 596: 597: if (!lastDataset) { 598: lastDataset = dataset; 599: lastTimestamp = timestamp; 600: } 601: 602: dTime = abs(timestamp - lastTimestamp); 603: dDist = distance - lastDistance; 604: calcSpeed = abs(dDist/dTime); 605:

Appendix A - Source Code

ModelGenerator.cpp, p10/12

606: if (((calcSpeed/ speed) < 0.1) && (distance > 100.0)) { 607: inter->newTrace(); 608: } else if ((dTime > 300) || (lastDataset != dataset)) { 609: inter->newTrace(); 610: } 611: 612: inter->add(TDSTuple(timestamp,distance,speed,road)); 613: 614: lastDataset = dataset; 615: lastTimestamp = timestamp; 616: lastDistance = distance; 617: 618: } 619: } 620: 621: } catch (const BadQuery& er) { 622: cout << endl << "!!! ERROR:" << er.what() << endl 623: << \t << query.str() << endl; 624: } 625: 626: currentModel->add(inter); 627: inter->saveTraces(); 628: // inter->buildTable(); 629: 630: } 631: 632: void ModelGenerator::guessIntersection(unsigned long id) { 633: /* 634: guessIntersection scans for points around an 635: intersection and guesses where the true center 636: of the intersection is from gps data. 637: 638: Line crossing formula from: 639: http://bloggingmath.wordpress.com/2009/05/29/line-segment-intersection/ 640: */ 641: Query query = db->getConnection()->query(); 642: query << "SELECT " 643: << "mn.easting as is_easting, mn.northing as is_northing, " 644: << "p.easting, p.northing, p.dataset_id, UNIX_TIMESTAMP(p.point_time) as ts " 645: << "FROM map_node mn " 646: << "LEFT JOIN raw_point p " 647: << "ON ((p.northing BETWEEN mn.northing - 50 AND mn.northing + 50) " 648: << "AND (p.easting BETWEEN mn.easting - 50 AND mn.easting + 50)) " 649: << "WHERE mn.id=" << id 650: << " AND p.speed > 1 ORDER BY point_time, dataset_id"; 651: 652: //initialise stored info 653: double t, origy, origx, px, py, sDist, rDist, currentHeading, newHeading, 654: headingDiff, count; 655: double minDist = 5.0; 656: px = py = count = 0.0; 657: Point2D s,r; 658: 659: try { 660: StoreQueryResult res = query.store(); 661: int numRows = res.num_rows(); 662: 663: if (res && numRows) { 664: //first load all data into arrays 665: Point2D* pts = new Point2D[numRows]; 666: sql_bigint_unsigned* timestamp = new sql_bigint_unsigned[numRows]; 667: 668: stringstream ss; 669: ss << "guessIntersection" << id << ".dat"; 670: 671: ofstream f; 672: f.open(ss.str().c_str()); 673:

Appendix A - Source Code

ModelGenerator.cpp, p11/12

674: for (int i = 0; i < numRows; i++) { 675: pts[i](res[i]["easting"],res[i]["northing"]); 676: timestamp[i] = res[i]["ts"]; 677: } 678: origx = res[0]["is_easting"]; 679: origy = res[0]["is_northing"]; 680: 681: //then compare with other candidates for line crossings (ie intersection) 682: for (int i = 0; i < numRows-3; i++) { 683: 684: sDist = Toolbox::calculateDistance(pts[i],pts[i+1]); 685: if (abs(timestamp[i] - timestamp[i+1]) < 60 && sDist > minDist) { 686: //get current heading 687: currentHeading = Toolbox::calculateDirection(pts[i],pts[i+1]); 688: //get line vector 689: s(pts[i+1].x-pts[i].x,pts[i+1].y-pts[i].y); 690: //look at all other points 691: for (int j = i+2; j < numRows-1; j++) { 692: rDist = Toolbox::calculateDistance(pts[j],pts[j+1]); 693: //first check lines are continuous 694: if (abs(timestamp[j] - timestamp[j+1]) < 60 && rDist > minDist) { 695: newHeading = Toolbox::calculateDirection(pts[j],pts[j+1]); 696: //then check lines arent aligned 697: headingDiff = Toolbox::minDOrient(currentHeading,newHeading); 698: //get crossing line vector 699: r(pts[j+1].x-pts[j].x,pts[j+1].y-pts[j].y); 700: 701: if (( s.x*r.y - r.x*s.y ) > minDist*minDist && headingDiff > 70.0) { //we want fairly steep crossings 702: t = ( (pts[j].x-pts[i].x)*s.y-(pts[j].y-pts[i].y)*s.x ) / ( s.x*r.y - r.x*s.y ); 703: //check that the crossing lies inside line 1: 704: if (t < 1.0 && t > 0.0) { 705: t = ( (pts[j].x-pts[i].x)*r.y-(pts[j].y-pts[i].y)*r.x ) / ( s.x*r.y - r.x*s.y ); 706: //check the crossing lies inside line 2: 707: if (t < 1.0 && t > 0.0) { 708: f << fixed << pts[j].x+t*r.x-origx << " " << pts[j].y+t*r.y-origy 709: << " " << pts[i].x-origx << " " << pts[i].y-origy 710: << << headingDiff 711: << << Toolbox::calculateDistance(pts[j],pts[j+1]) << << Toolbox::calculateDistance(pts[i],pts[i+1]) 712: << endl; 713: 714: //add to the crossing points measure 715: px += pts[j].x+t*r.x; 716: py += pts[j].y+t*r.y; 717: count += 1.0; 718: } 719: } 720: } 721: } 722: } 723: } 724: } 725: 726: f.close(); 727: } 728: 729: } catch (const BadQuery& er) { 730: cout << endl << "!!! ERROR:" << er.what() << endl

Appendix A - Source Code 731: 732: 733: 734: 735: 736: 737: } 738: << \t << query.str() << endl; }

ModelGenerator.cpp, p12/12

if (count > 1.0) { currentModel->addCorrection(id, Point2D(px/count,py/count)); }

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: #include #include #include #include "Polygon.h" <math.h> <iostream> <iomanip>

Polygon.cpp, p1/2

using namespace std; Polygon::Polygon() { points = NULL; count = 0; } Polygon::Polygon(Point2D a, Point2D b, double spacing) { double rise = a.y - b.y; double run = a.x - b.x; double yflag = b.y > a.y ? -1 : 1; double xflag = b.x > a.x ? -1 : 1; //Distance along gradient, swap for normals double xx, yy; //Basis line for bounding box, longer than original line Point2D l0, l1; double gradient = 0.0; double normal = 0.0; if (rise == 0) { //horizontal line l0(a.x + xflag*spacing, a.y); l1(b.x - xflag*spacing, b.y); xx = 0; yy = spacing; } else if (run == 0) { //vertical line l0(a.x, a.y + yflag*spacing); l1(b.x, b.y - yflag*spacing); xx = spacing; yy = 0; } else { //general case gradient = rise / run; normal = -1.0 / gradient; xx = spacing/sqrt(normal*normal+1); yy = xx*normal; //operate on normals, use reverse xx/yy l0(a.x-(yflag*yy), a.y+(yflag*xx)); l1(b.x+(yflag*yy), b.y-(yflag*xx)); } // // // // // // // // // // // cout << "VALUES: gradient=" << gradient << ", normal=" << normal << ", yflag=" << yflag << ", xflag=" << xflag << ", xx=" << xx << ", yy=" << yy << endl; cout << "Newline: "; displayPoint(l0, "l0"); displayPoint(l1, "l1"); cout << endl; points = new Point2D[4]; count = 4; points[0].x points[0].y points[1].x points[1].y points[2].x = = = = = l0.x+xx; l0.y+yy; l1.x+xx; l1.y+yy; l1.x-xx;

Appendix A - Source Code 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: points[2].y = l1.y-yy; points[3].x = l0.x-xx; points[3].y = l0.y-yy; } Polygon::Polygon() { // if(points != NULL) { // delete[] points; // } } void Polygon::usePoints(Point2D* ps, int num) { points = ps; count = num; }

Polygon.cpp, p2/2

void Polygon::dump() { cout << setiosflags(ios::fixed) << setprecision(7); for(int i=0; i < count ; i++) { std::cout << points[i].x << << points[i].y << std::endl; } if (count) std::cout << points[0].x << << points[0].y << std::endl; }

Appendix A - Source Code

Projection.cpp, p1/2

1: #include "Projection.h" 2: #include "Toolbox.h" 3: 4: const long double Projection::a = 6378137.0; 5: const long double Projection::invF = 298.257223563; 6: const long double Projection::b = a * (1 - invF); 7: const long double Projection::f = 1 / invF; 8: const long double Projection::e2 = 2*f - f*f; 9: const long double Projection::e = sqrt(e2); 10: const long double Projection::e4 = e2*e2; 11: const long double Projection::e6 = e4*e2; 12: const long double Projection::es2 = e2 / (1 - e2); 13: const long double Projection::es = sqrt(es2); 14: const long double Projection::n = (a-b) / (a+b); 15: const long double Projection::n2 = n*n; 16: const long double Projection::n3 = n2*n; 17: const long double Projection::n4 = n3*n; 18: const long double Projection::g = a*(1-n)*(1-n2)*(1+(9*n2)/4+(225*n4)/64)*(M_PI/180); 19: const double Projection::falseEasting = 500000.0; 20: const double Projection::falseNorthing = 10000000.0; 21: const long double Projection::k0 = 0.9996; 22: const double Projection::zoneWidth = 6.0; //degrees 23: const double Projection::lonWestEdge = -186.0; 24: const double Projection::centralMeridianZone0 = -183.0; 25: 26: Point2D Projection::convertLLtoNE(Point2D ll) { 27: long double lat = DEGTORAD(ll.y); 28: long double lon = DEGTORAD(ll.x); 29: 30: double zoneReal = (ll.x - lonWestEdge) / zoneWidth; 31: int zone = trunc(zoneReal); 32: double centralMeridian = (zone*zoneWidth + centralMeridianZone0) * M_PI / 180.0; 33: 34: long double sinLat = sin(lat); 35: long double sin2Lat = sin(lat*2); 36: long double sin4Lat = sin(lat*4); 37: long double sin6Lat = sin(lat*6); 38: 39: long double diffLon = lon - centralMeridian; 40: long double diffLon2 = diffLon*diffLon; 41: long double diffLon3 = diffLon2*diffLon; 42: long double diffLon4 = diffLon3*diffLon; 43: long double diffLon5 = diffLon4*diffLon; 44: long double diffLon6 = diffLon5*diffLon; 45: long double diffLon7 = diffLon6*diffLon; 46: long double diffLon8 = diffLon7*diffLon; 47: 48: long double a0 = 1-(e2/4)-((3*e4)/64)-((5*e6)/256); 49: long double a21 = e2+(e4/4.0); // +((15.0*e6)/128.0)); 50: long double a22 = (15.0*e6)/128.0; 51: //long double a22 = (3.0/8.0)*(e2+(e4/4.0)+((15.0*e6)/128.0)); 52: long double a2 = (3.0/8.0)*(a21+a22); 53: long double a4 = (15.0/256.0)*(e4+((3*e6)/4)); 54: long double a6 = (35.0*e6)/3072; 55: 56: long double radiiRho = a*(1-e2)/pow((1-(e2*sinLat*sinLat)),1.5); 57: long double radiiNu = a/pow((1-(e2*sinLat*sinLat)),0.5); 58: 59: long double psi = radiiNu / radiiRho; 60: long double psi2 = psi*psi; 61: long double psi3 = psi2*psi; 62: long double psi4 = psi3*psi; 63: 64: long double meridianDist1 = a*lat*a0; 65: long double meridianDist2 = -a*sin2Lat*a2; 66: long double meridianDist3 = a*sin4Lat*a4; 67: long double meridianDist4 = -a*sin6Lat*a6;

Appendix A - Source Code

Projection.cpp, p2/2

68: long double meridianDist = meridianDist1 + meridianDist2 + meridianDist3 + meridianDist4; 69: 70: long double cosLat = cos(lat); 71: long double cosLat2 = cosLat*cosLat; 72: long double cosLat3 = cosLat2*cosLat; 73: long double cosLat4 = cosLat3*cosLat; 74: long double cosLat5 = cosLat4*cosLat; 75: long double cosLat6 = cosLat5*cosLat; 76: long double cosLat7 = cosLat6*cosLat; 77: 78: long double tanLat = tan(lat); 79: long double tanLat2 = tanLat*tanLat; 80: long double tanLat4 = tanLat2*tanLat2; 81: long double tanLat6 = tanLat4*tanLat2; 82: 83: long double easting1 = radiiNu*diffLon*cosLat; 84: long double easting2 = radiiNu*diffLon3*cosLat3*(psi-tanLat2)/6; 85: long double easting3 = radiiNu*diffLon5*cosLat5*(4*psi3*(1-6*tanLat2)+ 86: psi2*(1+8*tanLat2)-psi*(2*tanLat2)+tanLat4)/120; 87: long double easting4 = radiiNu*diffLon7*cosLat7*(61-479*tanLat2+179*tanLat4-tanLat6)/5040; 88: long double eastingSum = easting1+easting2+easting3+easting4; 89: long double easting = eastingSum*k0+falseEasting; 90: 91: long double northing1 = radiiNu*sinLat*diffLon2*cosLat/2; 92: long double northing2 = radiiNu*sinLat*diffLon4*cosLat3* 93: (4*psi2+psi-tanLat2)/24; 94: long double northing3 = radiiNu*sinLat*diffLon6*cosLat5*(8*psi4* 95: (11-24*tanLat2)-28*psi3*(1-6*tanLat2)+psi2* 96: (1-32*tanLat2)-psi*(2*tanLat2)+tanLat4)/720; 97: long double northing4 = radiiNu*sinLat*diffLon8*cosLat6*(1385-3111*tanLat2+ 98: 543*tanLat4-tanLat6)/40320; 99: long double northingSum = meridianDist + northing1 + northing2 + northing3 + northing4; 100: long double northing = northingSum*k0 + falseNorthing; 101: 102: return Point2D(easting, northing); 103: }

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: #include "Road.h" #include "Intersection.h" Road::Road(unsigned int id) { //ctor this->id = id; } Road::Road() { //dtor }

Road.cpp, p1/1

bool Road::usesIntersection(Intersection* i) { if (!i || (!a && !b)) return false; if (a && (a->getID() == i->getID())) return true; if (b && (b->getID() == i->getID())) return true; return false; }

Appendix A - Source Code

Stats.cpp, p1/4

1: #include "Stats.h" 2: #include "Database.h" 3: 4: #include <iostream> 5: #include <iomanip> 6: 7: #ifndef NULL 8: #define NULL 0L 9: #endif 10: 11: using namespace std; 12: using namespace mysqlpp; 13: 14: Stats* Stats::me = NULL; 15: 16: Stats::Stats() { 17: //ctor 18: } 19: 20: Stats::Stats() { 21: //dtor 22: } 23: 24: Stats* Stats::ptr() { 25: if (me == NULL) { 26: me = new Stats(); 27: } 28: 29: return me; 30: } 31: 32: Stats& Stats::ref() { 33: if (me == NULL) { 34: me = new Stats(); 35: } 36: 37: return *me; 38: } 39: 40: void Stats::addRoadSpeed(ubigint roadID, double speed) { 41: roadMidSpeeds[roadID].push_back(speed); 42: } 43: 44: void Stats::dump() { 45: cout << setprecision(3) << fixed; 46: cout << "STATS DUMP" << endl; 47: 48: cout << "[Road Speeds]" << endl; 49: for (CostList::iterator it = roadMidSpeeds.begin(); it != roadMidSpeeds.end(); it++) { 50: CostEntry entry = *it; 51: cout << "Road=" << entry.first << ":"; 52: list<double> & entries = entry.second; 53: entries.sort(); 54: for (list<double>::iterator itt = entries.begin(); itt != entries.end(); itt++) { 55: cout << (*itt) << ; 56: } 57: cout << " [" << getRoadSpeedGuess(entry.first) << ] << endl; 58: } 59: 60: cout << endl << "[Intersections]"; 61: for (IntersectionCosts::iterator it = intersectionCosts.begin(); it != intersectionCosts.end(); it++) { 62: IntersectionEntry entry = *it; 63: cout << endl << "Intersection=" << entry.first; 64: RouteCosts & routes = entry.second; 65: for (RouteCosts::iterator itt = routes.begin(); itt != routes.end(); itt++)

Appendix A - Source Code {

Stats.cpp, p2/4

66: RouteEntry routeEntry = *itt; 67: cout << endl << "\t Route:" << routeEntry.first.first << "->" << routeEntry.first.second << ":"; 68: list< pair<ubigint,double> > & costs = routeEntry.second; 69: costs.sort(); 70: for (list< pair<ubigint,double> >::iterator it3 = costs.begin(); it3 != costs.end(); it3++) { 71: cout << (*it3).second << " "; 72: } 73: } 74: } 75: 76: cout << endl << "[Road Costs]"; 77: for (TimeCostList::iterator it = roadCosts.begin(); it != roadCosts.end(); it++) { 78: TimeCostEntry entry = *it; 79: cout << endl << "Road=" << entry.first << ":"; 80: list< pair<ubigint,double> > & entries = entry.second; 81: entries.sort(); 82: for (list< pair<ubigint,double> >::iterator itt = entries.begin(); itt != entries.end(); itt++) { 83: cout << (*itt).second << ; 84: } 85: } 86: 87: } 88: 89: double Stats::getRoadSpeedGuess(ubigint roadID) { 90: CostList::iterator it = roadMidSpeeds.find(roadID); 91: if (it == roadMidSpeeds.end()) return 0.0; 92: 93: list<double> & entries = (*it).second; 94: 95: if (entries.size() == 0) { 96: return 0.0; 97: } else if (entries.size() < 5) { 98: double sum = 0.0; 99: long count = 0; 100: for (list<double>::iterator itt = entries.begin(); itt != entries.end(); itt++) { 101: sum += (*itt); 102: count++; 103: } 104: return (sum / count); 105: } else { 106: size_t ec = entries.size(); 107: size_t i=0; 108: double pc; 109: double sum = 0.0; 110: long count = 0; 111: entries.sort(); 112: for (list<double>::iterator itt = entries.begin(); itt != entries.end(); itt++) { 113: pc = (double)i / (double)ec; 114: if (pc >= 0.7 && pc <= 0.9) { 115: sum += (*itt); 116: count++; 117: } 118: i++; 119: } 120: return (sum / count); 121: 122: } 123: 124: } 125: 126: void Stats::addIntersectionCost(ubigint intersectionID, ubigint fromRoad, ubigint

Appendix A - Source Code

Stats.cpp, p3/4

toRoad, double cost, ubigint time) { 127: intersectionCosts[intersectionID][pair<ubigint,ubigint>(fromRoad,toRoad)].push_back(pair<ub igint,double>(time,cost)); 128: cout << "$$$ " << intersectionID << << fromRoad << : << toRoad 129: << << cost << @ << time << endl; 130: } 131: 132: void Stats::addRoadCost(ubigint road, double cost, ubigint time) { 133: roadCosts[road].push_back(pair<ubigint,double>(time,cost)); 134: cout << "$$$ road " << road << << cost <<endl; 135: } 136: 137: void Stats::writeToDB(int modelID) { 138: Database* db = new Database(); 139: db->connect(); 140: 141: try { 142: Query qryWipe = db->getConnection()->query(); 143: qryWipe << "DELETE FROM stat_road WHERE model_id = " << modelID; 144: StoreQueryResult res = qryWipe.store(); 145: } catch (BadQuery bq) { 146: return; 147: } 148: 149: Query qry = db->getConnection()->query(); 150: 151: // cout << "[Road Speeds]" << endl; 152: // for (CostList::iterator it = roadMidSpeeds.begin(); it != roadMidSpeeds.end(); it++) { 153: // CostEntry entry = *it; 154: // cout << "Road=" << entry.first << ":"; 155: // list<double> & entries = entry.second; 156: // entries.sort(); 157: // for (list<double>::iterator itt = entries.begin(); itt != entries.end(); itt++) { 158: // cout << (*itt) << ; 159: // } 160: // cout << " [" << getRoadSpeedGuess(entry.first) << ] << endl; 161: // } 162: 163: for (IntersectionCosts::iterator it = intersectionCosts.begin(); it != intersectionCosts.end(); it++) { 164: IntersectionEntry entry = *it; 165: // cout << endl << "Intersection=" << entry.first; 166: RouteCosts & routes = entry.second; 167: for (RouteCosts::iterator itt = routes.begin(); itt != routes.end(); itt++) { 168: RouteEntry routeEntry = *itt; 169: // cout << endl << "\t Route:" << routeEntry.first.first << "->" << routeEntry.first.second << ":"; 170: list< pair<ubigint,double> > & costs = routeEntry.second; 171: // costs.sort(); 172: for (list< pair<ubigint,double> >::iterator it3 = costs.begin(); it3 != costs.end(); it3++) { 173: try { 174: qry.reset(); 175: qry << "INSERT INTO stat_intersection SET " 176: << " model_id=" << modelID 177: << ", intersection_id=" << entry.first 178: << ", from_road=" << routeEntry.first.first 179: << ", to_road=" << routeEntry.first.second 180: << ", timestamp=" << (*it3).first 181: << ", cost=" << (*it3).second; 182: StoreQueryResult res = qry.store(); 183: } catch(BadQuery bq) { 184: cout << endl << "!!! ERROR:" << bq.what() << endl 185: << \t << qry.str() << endl;

Appendix A - Source Code 186: 187: 188: 189: 190: } 191: 192: for it++) { 193: 194: // 195: 196: // 197: entries.end(); 198: // 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: } 213: } } //cout << (*it3).second << " "; } }

Stats.cpp, p4/4

(TimeCostList::iterator it = roadCosts.begin(); it != roadCosts.end(); TimeCostEntry entry = *it; cout << endl << "Road=" << entry.first << ":"; list< pair<ubigint,double> > & entries = entry.second; entries.sort(); for (list< pair<ubigint,double> >::iterator itt = entries.begin(); itt != itt++) { cout << (*itt).second << ; try { qry.reset(); qry << "INSERT INTO stat_road SET " << " model_id=" << modelID << ", road_id=" << entry.first << ", timestamp=" << (*itt).first << ", cost=" << (*itt).second; StoreQueryResult res = qry.store(); } catch(BadQuery bq) { cout << endl << "!!! ERROR:" << bq.what() << endl << \t << qry.str() << endl; } }

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: #include "Toolbox.h" #include <cmath> using namespace std; Toolbox::Toolbox() { //ctor } Toolbox::Toolbox() { //dtor } double Toolbox::mod(double value, double modulus) { if (value > 0 && value < modulus) return value; //Not the most efficient way, but itll do double newValue = value; while (newValue < 0) newValue += modulus; while (newValue >= modulus) newValue -= modulus; return newValue; } double Toolbox::calculateOrientation(Point2D a, Point2D b) { return mod(calculateHeading(a,b), 180.0); } double Toolbox::calculateHeading(Point2D a, Point2D b) { double lat1 = DEGTORAD(a.y); double lat2 = DEGTORAD(b.y); double lon1 = DEGTORAD(a.x); double lon2 = DEGTORAD(b.x);

Toolbox.cpp, p1/1

double rad = mod(atan2(sin(lon2-lon1)*cos(lat2),cos(lat1)*sin(lat2) -sin(lat1)*cos(lat2)*cos(lon2-lon1)), 2.0*M_PI); return RADTODEG(rad); } double Toolbox::calculateDirection(Point2D a, Point2D b) { double rad = atan2(b.x-a.x, b.y-a.y); return mod(450.0-(rad*180.0/M_PI),360.0); } double Toolbox::calculateDistance(const Point2D& a, const Point2D& b) { return sqrt((b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y-a.y)); } double Toolbox::minDOrient(double a, double b) { if (a == -1 || b == -1 ) return -1; //-1 = undefined a = mod(a, 180.0); b = mod(b, 180.0); double delta = abs(a-b); return min(delta, 180-delta); }

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: endl; 42: 43: endl 44: 45: 46: endl 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: #include "Trace.h" #include <cmath> #include <map> using namespace std; Trace::Trace() { inSpeed = outSpeed = -1; minDist = 9999999; maxDist = -minDist; inRoad = outRoad = delayA = delayB = maxIn = } Trace::Trace() { //dtor }

Trace.cpp, p1/5

maxOut = 0;

ostream &operator<<(ostream &stream, const Trace& o) { stream << "#DEBUG: getMinDist() = " << o.getMinDist() << ", tds.size() = " << o.tds.size() << ", inSpeed=" << o.inSpeed << ", outSpeed=" << o.outSpeed << ", minDist=" << o.minDist << ", maxDist=" << o.maxDist << ", inRoad=" << o.inRoad << ", outRoad=" << o.outRoad << ", delay=" << o.delayA << + << o.delayB << = << o.getDelay() << endl; stream << "#slowDownPoint (T0)" << endl << o.slowDownPoint.time << << o.slowDownPoint.distance << << o.slowDownPoint.speed - 1 << " 0" << endl << o.slowDownPoint.time << << o.slowDownPoint.distance << << o.slowDownPoint.speed + 1 << " 0" << endl << endl; stream << "#speedUpPoint (T1)" << endl << o.speedUpPoint.time << << o.speedUpPoint.distance << << o.speedUpPoint.speed - 1 << " 0" << endl << o.speedUpPoint.time << << o.speedUpPoint.distance << << o.speedUpPoint.speed + 1 << " 0" << endl << endl; stream << "#TX " << o.middlePoint.distance << << o.middlePoint.speed << stream << "#inSpeed & outspeed" << endl; stream << o.middlePoint.time << << o.minDist << << o.inSpeed << " 0" << << o.middlePoint.time << " 0 " << o.inSpeed << " 0" << endl << endl << o.middlePoint.time << << o.maxDist << << o.outSpeed << " 0" << << o.middlePoint.time << " 0 " << o.outSpeed << " 0" << endl << endl; stream << "#time distance speed road_id" << std::endl; TDSTuple t; list<TDSTuple>::const_iterator it = o.tds.begin(); for(it = o.tds.begin(); it != o.tds.end(); it++) { t = (*it); stream << t << std::endl; } return stream; } bool Trace::isBad() const { //data isnt near the intersection if (abs(getMinDist()) > 40.0) return true; //Small dataset = useless if (tds.size() < 10) return true;

Appendix A - Source Code 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: //One sided graphs = useless if (minDist <= 0 && maxDist >= 0) { if (minDist > -50) return true; if (maxDist < 50) return true; } return false; }

Trace.cpp, p2/5

void Trace::add(TDSTuple tuple) { if (tuple.location.x > 0) { if (minBound.x == 0) { minBound = tuple.location; } else { if (tuple.location.x < minBound.x) minBound.x = tuple.location.x; if (tuple.location.x > maxBound.x) maxBound.x = tuple.location.x; if (tuple.location.y < minBound.y) minBound.y = tuple.location.y; if (tuple.location.y > maxBound.y) maxBound.y = tuple.location.y; } } tds.push_back(tuple); } double Trace::getMinDist() const { double minDist = 9999999.0; TDSTuple t; list<TDSTuple>::const_iterator it; for(it = tds.begin(); it != tds.end(); it++) { t = (*it); if (abs(t.distance) < minDist) minDist = abs(t.distance); } return minDist; } void Trace::calculate() { //Find Middle point // Set distance to something out of bounds middlePoint.distance = 99999.9; maxDist = minDist = 0; TDSTuple t; list<TDSTuple>::iterator it; for(it = tds.begin(); it != tds.end(); it++) { t = (*it); if (t.distance < middlePoint.distance) middlePoint = t; } list<TDSTuple> tempList; bool midpointReached = false; double a = 0; double b = 0; int ca = 0; int cb = 0; //Rebuild list using distances -ve / +ve and offset of middlePoint.distance for(it = tds.begin(); it != tds.end(); it++) { t = (*it);

Appendix A - Source Code 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: if (!midpointReached && t.distance > middlePoint.distance) { t.distance *= -1; //t.distance += middlePoint.distance; tempList.push_back(t); if (t.speed > maxIn) maxIn = t.speed; } else { midpointReached = true; //t.distance -= middlePoint.distance; tempList.push_back(t); if (t.speed > maxOut) maxOut = t.speed; } if (t.distance < minDist) minDist = t.distance; if (t.distance > maxDist) maxDist = t.distance; } tds.clear(); tds = tempList; map<unsigned long, long> inRoadTally; map<unsigned long, long> outRoadTally; for(it = tds.begin(); it != tds.end(); it++) { t = (*it); if (t.distance < 0) { if ((t.speed / maxIn) > 0.8) { a +=t.speed; ca++; } } else { if ((t.speed / maxOut) > 0.8) { b +=t.speed; cb++; } } if (abs(t.distance) > 20) { // avoid noise at intersection if (t.distance < 0 ) { inRoadTally[t.road]++; } else { outRoadTally[t.road]++; } } } long maxValue = 0; map<unsigned long, long>::iterator mip; pair<unsigned long, long> p; for(mip = inRoadTally.begin(); mip != inRoadTally.end(); mip++) { p = (*mip); if (p.second > maxValue) { maxValue = p.second; inRoad = p.first; } }

Trace.cpp, p3/5

maxValue = 0; for(mip = outRoadTally.begin(); mip != outRoadTally.end(); mip++) { p = (*mip); if (p.second > maxValue) { maxValue = p.second; outRoad = p.first; } } //rough as guts calculations, lets us know where we should look above. inSpeed = a / ca; outSpeed = b / cb;

Appendix A - Source Code

Trace.cpp, p4/5

202: 203: // Set our iterator to the midpoint for searching for T0/T1 204: bool found=false; 205: it = tds.begin(); 206: list<TDSTuple>::iterator it2(it); 207: while (it != tds.end() && !found) { 208: t = (*it); 209: if (t == middlePoint) { 210: found = true; 211: break; 212: } 213: it++; 214: it2++; 215: } 216: 217: //If we found the midpoint again, we can search for T0/T1 218: if (found) { 219: t = (*it); 220: bool above = false; 221: 222: //Find slowdown point 223: double lastSpeed = t.speed; 224: 225: //get it under the line 226: while (it != tds.begin() && (t.speed > inSpeed)) { 227: it--; 228: t = (*it); 229: } 230: 231: while (it != tds.begin() && ((t.speed > lastSpeed) || ((t.speed < inSpeed) && !above) ) ) { 232: if (t.speed > inSpeed) above = true; 233: lastSpeed = t.speed; 234: slowDownPoint = t; 235: it--; 236: t = (*it); 237: } 238: 239: t = (*it2); 240: //get under the line 241: while (it2 != tds.end() && (t.speed > outSpeed)) { 242: it2++; 243: t = (*it2); 244: } 245: 246: lastSpeed = t.speed; 247: above = false; 248: while (it2 != tds.end() && ((t.speed > lastSpeed) || ((t.speed < outSpeed) && !above))) { 249: if (t.speed > outSpeed) above = true; 250: lastSpeed = t.speed; 251: speedUpPoint = t; 252: it2++; 253: t = (*it2); 254: } 255: 256: if ((slowDownPoint.speed > 0) && 257: (speedUpPoint.speed > 0)) { 258: delayA = middlePoint.time - slowDownPoint.time 259: (abs(slowDownPoint.distance) / inSpeed); 260: delayB = speedUpPoint.time - middlePoint.time 261: (abs(speedUpPoint.distance) / outSpeed); 262: } 263: 264: } 265: 266: } 267:

Appendix A - Source Code 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: #if(0) double Trace::calculateSpeed() { double threshold = 0.9; list<TDSTuple>::iterator it; double distMin = -1.0; TDSTuple maxSpeed; TDSTuple p; for(it = tds.begin(); it != tds.end(); it++) { p = (*it); if (p.y > maxSpeed.y) p = maxSpeed; } double sum=0.0; int count=0; double ratio; for(it = tds.begin(); it != tds.end(); it++) { p = (*it); ratio = p.y / maxSpeed.y; if (p.y > threshold) { sum+=p.y; count++; } } return (sum/count); } #endif

Trace.cpp, p5/5

Appendix A - Source Code

Trip.cpp, p1/7

1: #include "Trip.h" 2: #include "Model.h" 3: #include "Toolbox.h" 4: #include "Database.h" 5: #include "Data.h" 6: #include "Stats.h" 7: 8: #include <sstream> 9: #include <fstream> 10: 11: Trip::Trip(Model* m) { 12: model = m; 13: } 14: 15: Trip::Trip() 16: { 17: //dtor 18: } 19: 20: ostream &operator<<(ostream &stream, const Trip& o) { 21: stream << "[Trip of size " << o.points.size() << "]" << endl; 22: 23: Point* p; 24: 25: sql_bigint_unsigned id, near; 26: double dist = 0; 27: for(map<sql_bigint_unsigned, sql_bigint_unsigned>::const_iterator it = o.closestIntersection.begin(); 28: it != o.closestIntersection.end(); it++) { 29: id = (*it).first; 30: // o.closestDistance[ id ]; 31: map<sql_bigint_unsigned, double>::const_iterator itf = o.closestDistance.find(id); 32: dist = (*itf).second; 33: near = (*it).second; 34: stream << fixed << "id:" << id << " near:" << near << " dist:" << dist; 35: if (near) { 36: p = o.model->interPoints[near]; 37: stream << << p->coords; 38: // stream << o.points[near]; 39: } 40: stream << endl; 41: 42: } 43: 44: return stream; 45: } 46: 47: void Trip::add(Point* p) { 48: if (!p) return; 49: 50: points[p->id] = p; 51: // points.push_back(p); 52: } 53: 54: Point* Trip::findNextNonZero(int pointID) { 55: map<int, Point*>::iterator it = points.find(pointID); 56: if (it == points.end()) return NULL; 57: it++; 58: 59: while ((it != points.end()) && (((*it).second)->nearRoad == 0)) it++; 60: 61: return NULL; 62: } 63: 64: void Trip::mapToIntersections(Model* m) { 65: 66: sql_bigint_unsigned i=0;

Appendix A - Source Code

Trip.cpp, p2/7

67: double d; 68: 69: for(map<int, Point*>::iterator it = points.begin(); it != points.end(); it++ ) { 70: i = ((*it).second)->id; 71: closestDistance[i] = 0.0; 72: closestIntersection[i] = 0; 73: for(map<unsigned long, Point*>::iterator it2 = m->interPoints.begin(); 74: it2 != m->interPoints.end(); it2++) { 75: d = Toolbox::calculateDistance(((*it).second)->coords, ((*it2).second)->coords); 76: if (( closestDistance[i] == 0.0 || d < closestDistance[i]) && d < 100.0 ) { // 77: closestDistance[i] = d; 78: closestIntersection[i] = ((*it2).second)->id; 79: } 80: } 81: } 82: 83: double minDist = 99999.9; 84: sql_bigint_unsigned id = 0; 85: double d2; 86: sql_bigint_unsigned i2; 87: 88: map<int, Point*>::iterator temp = points.end(); 89: // temp--; 90: // i2 = (*temp).second->id; 91: // temp++; 92: 93: long max = points.size(); 94: long current = 0; 95: long delta = max / 10; 96: long count = 0; 97: cout << endl << "Finding interesections for trip: "; 98: cout.flush(); 99: for(map<int, Point*>::iterator it = points.begin(); it != temp; it++ ) { 100: current++; 101: i = ((*it).second)->id; 102: d = closestDistance[i]; 103: if (d == 0.0 || (closestIntersection[i] != closestIntersection[i2]) ) { 104: if (id) { 105: do { 106: it--; 107: i = ((*it).second) ? ((*it).second)->id : 0; 108: } while ((closestDistance[i] > 0.0) && 109: (closestIntersection[i] == closestIntersection[id]) && 110: (it != points.begin())); 111: 112: it++; 113: i = ((*it).second)->id; 114: while((closestDistance[i] > 0.0) && 115: (closestIntersection[i] == closestIntersection[id]) && 116: (it != points.end())) { 117: if (i != id) { 118: closestDistance[i] = 0.0; 119: closestIntersection[i] = 0; 120: } else { 121: path.push_back(i); 122: } 123: it++; 124: i = ((*it).second)->id; 125: } 126: count++; 127: } else { 128: it++; 129: } 130: minDist = 99999.9; 131: id = 0; 132: } else if (d < minDist) {

Appendix A - Source Code

Trip.cpp, p3/7

133: id = i; 134: minDist = d; 135: } 136: 137: i2 = i; 138: 139: if (!(current % delta)) { 140: cout << (current*100 / max) << "%.."; 141: cout.flush(); 142: } 143: 144: if (it == temp) { 145: break; 146: } 147: } 148: cout << "Done (" << count << ")" << endl; 149: } 150: 151: void Trip::checkPath() { 152: Database* db = new Database(); 153: db->connect(); 154: 155: vector<sql_bigint_unsigned>::iterator it; 156: sql_bigint_unsigned inter,lastInter; 157: inter = lastInter = 0; 158: sql_bigint_unsigned road; 159: long id, lastID; 160: id = lastID = 0; 161: 162: for (it = path.begin(); it != path.end(); it++) { 163: id = *it; 164: inter = closestIntersection[id]; 165: if (lastInter) { 166: Query qry = db->getConnection()->query(); 167: qry << "SELECT id FROM model_road " 168: << "WHERE (node_from = " << lastInter << " AND node_to = " << inter << ") " 169: << "OR (node_to = " << lastInter << " AND node_from = " << inter << ")"; 170: 171: int rows = 0; 172: road = 0; 173: try { 174: StoreQueryResult res = qry.store(); 175: 176: if (res) { 177: rows = res.num_rows(); 178: if (rows) road = res[0]["id"]; 179: } 180: 181: } catch (const Exception& er) { 182: cout << endl << "!!! ERROR:" << er.what() << endl 183: << \t << qry.str() << endl; 184: } 185: 186: cout << fixed << "Find road containing " << lastInter << " and " << inter << ", " 187: << rows << "matches: road=" << road << endl; 188: if (road) { 189: Part* pp = new Part(); 190: pp->fromIndex = lastID; 191: pp->fromIntersectionID = lastInter; 192: pp->roadID = road; 193: pp->length = 0; 194: pp->midSpeed = 0; 195: pp->toIndex = id+1; 196: pp->toIntersectionID = inter; 197: parts.push_back(pp);

Appendix A - Source Code 198: } 199: } 200: 201: lastID = id; 202: lastInter = inter; 203: } 204: 205: db->disconnect(); 206: delete db; 207: } 208: 209: void Trip::walkParts() { 210: Part* currentPart; 211: Part* lastPart; 212: Point* point; 213: list<Part*>::iterator it; 214: map<int, Point*>::const_iterator poit; 215: map<int, Point*>::const_iterator stop; 216: lastPart = NULL; 217: for (it = parts.begin(); it != parts.end(); it++) { 218: currentPart = *it; 219: long maxCount=0; 220: currentPart->maxSpeed = 0.0; 221: stop = points.find(currentPart->toIndex); 222: for(poit = points.find(currentPart->fromIndex); 223: poit != points.end() && poit != stop; poit++) { 224: maxCount++; 225: } 226: double sum = 0; 227: long count = 0; 228: long i = 0; 229: for(map<int, Point*>::const_iterator poit = points.find(currentPart->fromIndex); 230: poit != points.end() && poit != stop; poit++) { 231: point = (*poit).second; 232: if ((i >= maxCount / 4) && 233: (i <= (3*maxCount)/4)) { 234: sum += point->speed; 235: count++; 236: } 237: if (point->speed > currentPart->maxSpeed) { 238: currentPart->maxSpeed = point->speed; 239: } 240: i++; 241: } 242: 243: cout << "### " << currentPart->roadID << " avg speed=" 244: << sum << / << count << "= " << (sum/count) << endl; 245: 246: currentPart->midSpeed = (sum/count); 247: 248: Stats::ref().addRoadSpeed(currentPart->roadID, sum/count); 249: 250: lastPart = currentPart; 251: } 252: } 253: 254: void Trip::calcCosts() { 255: Part* currentPart; 256: Part* lastPart; 257: Point* point, *oldPoint; 258: list<Part*>::iterator it; 259: map<int, Point*>::const_iterator poit; 260: map<int, Point*>::const_iterator start,stop; 261: lastPart = NULL; 262: double integrationA, integrationB; 263: double manualDist = 0.0; 264: double dbDist, d, scale;

Trip.cpp, p4/7

Appendix A - Source Code

Trip.cpp, p5/7

265: double stdSpeed, stdWeights, weight; 266: for (it = parts.begin(); it != parts.end(); it++) { 267: currentPart = *it; 268: integrationA = integrationB = 0.0; 269: stdSpeed = stdWeights = 0.0; 270: sql_bigint_unsigned time = 0; 271: oldPoint = NULL; 272: 273: //verify that were not running off the end 274: if (points.find(currentPart->toIndex) == points.end()) { 275: currentPart->toIndex--; 276: } 277: 278: stop = points.find(currentPart->toIndex); 279: 280: manualDist = 0; 281: for(map<int, Point*>::const_iterator ppit = points.find(currentPart->fromIndex); 282: ppit != points.end() && ppit != stop; ppit++) { 283: point = (*ppit).second; 284: if (!time) time = point->time; 285: cout << "@@@ " << currentPart->roadID << << point->time - time 286: << << point->speed << << manualDist << << point->time << endl; 287: if (oldPoint) { 288: manualDist += Toolbox::calculateDistance(point->coords, oldPoint->coords ); 289: weight = abs(point->speed - oldPoint->speed) / Toolbox::max(point->speed, oldPoint->speed); 290: weight = (1 / (weight*weight+1)) + point->speed/20.0; 291: stdSpeed += point->speed * weight; 292: stdWeights += weight; 293: } 294: oldPoint = point; 295: } 296: 297: // stdSpeed /= stdWeights; 298: stdSpeed = currentPart->maxSpeed / 0.95; 299: 300: currentPart->roadCost = manualDist / stdSpeed; 301: 302: dbDist = Data::ref().getRoadLength(currentPart->roadID); 303: oldPoint = NULL; 304: 305: // COST OUT INTERSECTIONS A (from previous) & B (to next) COSTS. 306: // stop++; stop++; 307: //Integrate part A 308: for(map<int, Point*>::const_iterator ppit = points.find(currentPart->fromIndex); 309: ppit != points.end() && ppit != stop; ppit++) { 310: point = (*ppit).second; 311: // if (point->speed > stdSpeed) break; 312: // if (point->speed > currentPart->maxSpeed*0.9) break; 313: if (point->speed > currentPart->maxSpeed*0.95) break; 314: if (oldPoint) { 315: d = Toolbox::calculateDistance(point->coords, oldPoint->coords ); 316: // d = (Toolbox::diff(point->time, oldPoint->time)*(point->speed + oldPoint->speed)*0.5); 317: integrationA += d; 318: currentPart->t0 = Toolbox::diff(point->time, time); 319: } 320: oldPoint = point; 321: } 322: 323: //Integrate part B 324: start = points.find(currentPart->toIndex); 325: stop = points.find(currentPart->fromIndex); 326: oldPoint = NULL;

Appendix A - Source Code

Trip.cpp, p6/7

327: // stop--; stop--; 328: for(map<int, Point*>::const_iterator ppit = start; 329: ppit != points.begin() && ppit != stop; ppit--) { 330: point = (*ppit).second; 331: // if (point->speed > stdSpeed) break; 332: // if (point->speed > currentPart->maxSpeed*0.9) break; 333: if (point->speed > currentPart->maxSpeed*0.95) break; 334: if (oldPoint) { 335: d = Toolbox::calculateDistance(point->coords, oldPoint->coords ); 336: // integrationB += (Toolbox::diff(point->time, oldPoint->time)*(point->speed + oldPoint->speed)*0.5); 337: integrationB += d; 338: currentPart->t1 = Toolbox::diff(point->time, time); 339: } 340: oldPoint = point; 341: } 342: 343: if (!currentPart->length) { 344: currentPart->length = manualDist; 345: } 346: 347: map<int, Point*>::const_iterator a = points.find(currentPart->fromIndex); 348: map<int, Point*>::const_iterator b = points.find(currentPart->toIndex); 349: currentPart->time = Toolbox::diff(((*b).second)->time, ((*a).second)->time); 350: currentPart->costA = currentPart->t0 ? currentPart->t0 - integrationA / stdSpeed : 0; 351: currentPart->costB = currentPart->t1 ? 352: Toolbox::diff(currentPart->time, currentPart->t1) (integrationB / stdSpeed) 353: : 0; 354: scale = currentPart->costError() / currentPart->totalCost(); 355: cout << "--- IC (pre_scaling) a:" << currentPart->costA 356: << " b:" << currentPart->costB 357: << " r:" << currentPart->roadCost 358: << " sigma:" << currentPart->totalCost() 359: << " total:" << currentPart->time 360: << " Diff:" << currentPart->costError() 361: << ", " << scale*100 << "%" 362: << endl; 363: currentPart->costA *= (1+scale); 364: currentPart->costB *= (1+scale); 365: currentPart->roadCost *= (1+scale); 366: 367: if (lastPart) { 368: //If our parts marry up ... 369: if (lastPart->toIntersectionID == currentPart->fromIntersectionID ) { 370: cout << "--- Dist for " << currentPart->roadID << " " << currentPart->length 371: << " v:" << stdSpeed << / << currentPart->midSpeed << " max:" << currentPart->maxSpeed 372: << " |DB|:" << dbDist << " ratio:" << dbDist / currentPart->length 373: << << currentPart->roadCost << endl; 374: cout << "--- " << currentPart->roadID << 375: << integrationA << @ << currentPart->t0 << + 376: << integrationB << @ << currentPart->t1 << "+x=" << manualDist << endl; 377: 378: //Include stats info 379: Stats::ref().addIntersectionCost(currentPart->fromIntersectionID, lastPart->roadID, currentPart->roadID, lastPart->costB + currentPart->costA, time ); 380: Stats::ref().addRoadCost(currentPart->roadID, currentPart->roadCost, time); 381: 382: if (integrationA > manualDist || integrationB > manualDist) { 383: cout << "--- FFS, integration was borked" << endl; 384: } 385: } else {

Appendix A - Source Code

Trip.cpp, p7/7

386: cout << "--- MISS: " << lastPart->toIntersectionID << / << lastPart->fromIntersectionID 387: << << currentPart->fromIntersectionID << / << currentPart->toIntersectionID 388: << endl; 389: } 390: 391: cout << "--- " << currentPart->roadID << " time " << currentPart->time 392: << " SLD: " << Toolbox::calculateDistance(((*a).second)->coords,((*b).second)->coords) 393: << endl; 394: 395: } 396: 397: lastPart = currentPart; 398: cout << "@@@ " << currentPart->roadID << endl; 399: } 400: } 401: 402: void Trip::write(int id) { 403: stringstream ss; 404: ss << "trip_p" << fixed << id << ".dat"; 405: 406: ofstream f; 407: f.open(ss.str().c_str()); 408: Point* p; 409: sql_bigint_unsigned lastTime = 0; 410: for(map<int,Point*>::const_iterator it = points.begin(); it != points.end(); it++) { 411: p = (*it).second; 412: f << fixed << p->id << << p->coords.x << << p->coords.y << << p->time 413: << << (p->time - lastTime) << << p->heading << endl; 414: lastTime = p->time; 415: } 416: 417: f.close(); 418: 419: ss.str(""); 420: ss << "trip_i" << fixed << id << ".dat"; 421: 422: sql_bigint_unsigned near; 423: ofstream fi; 424: fi.open(ss.str().c_str()); 425: 426: for(map<sql_bigint_unsigned, sql_bigint_unsigned>::const_iterator it = closestIntersection.begin(); 427: it != closestIntersection.end(); it++) { 428: id = (*it).first; 429: map<sql_bigint_unsigned, double>::const_iterator itf = closestDistance.find(id); 430: near = (*it).second; 431: p = model->findIterPoint(near); 432: if (p) { 433: fi << fixed << id << << p->coords.x << << p->coords.y << << p->id << endl; 434: } 435: } 436: fi.close(); 437: }

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: #ifndef __DATABASE_H #define __DATABASE_H #define #define #define #define DB_HOST DB_NAME DB_USER DB_PASS "localhost" "yozeus" "yozeus" "y0zeus54"

Database.h, p1/1

#include <mysql++/mysql++.h> #include "Point2D.h" using namespace mysqlpp; class Database { public: /** Default constructor */ Database(); /** Default destructor */ virtual Database(); /** Connect to default data source */ bool connect(); bool isConnected() { return connected; } void disconnect(); bool updateMapNode(sql_bigint_unsigned node, Point2D northingEasting); bool updateRawPoint(sql_bigint_unsigned point, Point2D northingEasting); Connection* getConnection() { return con; } void addRoad(sql_bigint_unsigned model, sql_bigint_unsigned way, sql_bigint_unsigned nodeFrom, sql_bigint_unsigned nodeTo, sql_bigint_unsigned seqFrom, sql_bigint_unsigned seqTo); void deleteRoads(int model); void deletePoints(int model); bool addPoint(int model, sql_bigint_unsigned point, sql_bigint_unsigned road, double heading); void startTransaction(); void commit(); protected: private: bool connected; Connection* con; Transaction* trans;

}; #endif // __DATABASE_H

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: #ifndef __DATA_H #define __DATA_H #include <map> #include "Database.h" using namespace std; using namespace mysqlpp; class Data { public: static Data* ptr(); static Data& ref(); virtual Data(); double getRoadLength(long id);

Data.h, p1/1

protected: private: Data(); void getConnection(); Database* db; static Data* me; map<long, double> roadLength; }; #endif // __DATA_H

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: #ifndef __INTERSECTION_H #define __INTERSECTION_H #include #include #include #include <map> "Road.h" "Trace.h" "TDSTuple.h"

Intersection.h, p1/1

class Intersection { public: /** Default constructor */ Intersection(unsigned int id); /** Default destructor */ virtual Intersection(); void addRoad(Road* r); Road* findRoad(unsigned int id); unsigned int getID() { return id; } /** Create a new trace to use */ void newTrace(); /** Add the specified tuple to the current trace */ void add(TDSTuple tuple); void dumpTraces(); void saveTraces(); void buildTable(); protected: private: unsigned int id; std::map<unsigned int, Road*> roads; list<Trace*> traces; Trace* currentTrace;

// // // //

// // };

#endif // __INTERSECTION_H

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: #ifndef __LAZYTYPES_H #define __LAZYTYPES_H #include <mysql++/mysql++.h> //TLDT typedef mysqlpp::sql_bigint_unsigned ubigint; #endif

LazyTypes.h, p1/1

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: #ifndef __MODELGENERATOR_H #define __MODELGENERATOR_H #include <list> #include "Database.h" #include "Model.h" #ifndef NULL #define NULL 0 #endif class ModelGenerator { public: /** Default constructor */ ModelGenerator(); /** Default destructor */ virtual ModelGenerator(); void build(int modelID); void assignNorthingEasting(); void buildRoads(); void matchPoints(); void loadSegments(); void loadPoints(); void checkPoints(); void savePoints(); void makeTrips(); void processIntersections(); void processIntersection(unsigned long id); void guessIntersection(unsigned long id); double calculateSpeed(list<Point2D> &distanceSpeed); protected: private: int assignNorthingEastingNodes(int limit); int assignNorthingEastingPoints(int limit); Database* db; Model* currentModel; }; #endif // __MODELGENERATOR_H

ModelGenerator.h, p1/1

// //

Appendix A - Source Code

Model.h, p1/1

1: #ifndef __MODEL_H 2: #define __MODEL_H 3: 4: #include <list> 5: #include <map> 6: #include "Segment.h" 7: #include "Point.h" 8: #include "Road.h" 9: #include "Intersection.h" 10: #include "Trip.h" 11: 12: using namespace std; 13: 14: class Model { 15: 16: public: 17: /** Default constructor */ 18: Model(int id); 19: /** Default destructor */ 20: virtual Model(); 21: int getID() { return id; }; 22: 23: list<Segment*>& getSegments() { return segments; } 24: list<Point*>& getPoints() { return points; } 25: const list<Trip*>& getTrips() { return trips; } 26: const list<pair<unsigned long, Point2D> >& getCorrections() { return corrections; } 27: Road* findRoad(unsigned int id); 28: 29: void dumpSegments(); 30: void writeSegments(); 31: void writePoints(); 32: void writeTrips(); 33: 34: void add(Intersection*); 35: void add(Trip*); 36: 37: void addCorrection(unsigned long intersectionID, Point2D offset); 38: 39: map<unsigned long, Point*> interPoints; 40: Point* findIterPoint(unsigned long id); 41: 42: private: 43: list<Segment*> segments; 44: list<Point*> points; 45: list<Trip*> trips; 46: list<pair<unsigned long, Point2D> > corrections; 47: map<unsigned int, Road*> roads; 48: map<unsigned int, Intersection*> intersections; 49: 50: int id; 51: }; 52: 53: #endif // __MODEL_H

Appendix A - Source Code 1: #ifndef __PART_H 2: #define __PART_H 3: 4: #include <mysql++/sql_types.h> 5: 6: /* _ road 7: / 8: O------------>O 9: \_ from* \_ to* 10: 11: 12: 13: | ____/\_ 14: |---/+-----+\_ 15: | / | | \____ 16: | / | | \ 17: ===================== 18: 19: */ 20: struct Part { 21: sql_bigint_unsigned fromIntersectionID; 22: int fromIndex; 23: sql_bigint_unsigned t0; 24: double costA; 25: 26: sql_bigint_unsigned roadID; 27: double midSpeed; // speed taken using the middle of rhte road; 28: double maxSpeed; 29: double length; //in metres 30: sql_bigint_unsigned time; 31: 32: /** cost assigned to this road, for this part of a trip, neglecting intersection times. */ 33: double roadCost; 34: 35: sql_bigint_unsigned toIntersectionID; 36: int toIndex; 37: sql_bigint_unsigned t1; 38: double costB; 39: 40: Trip* trip; 41: 42: double totalCost() { return costA+costB+roadCost;} 43: double costError() { return time - totalCost(); } 44: 45: }; 46: 47: #endif //__PART_H

Part.h, p1/1

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: #ifndef __POINT2D_H #define __POINT2D_H #include <ostream>

Point2D.h, p1/1

struct Point2D { friend std::ostream &operator<<(std::ostream &stream, const Point2D& p) { stream << std::fixed << "(x=" << p.x << ",y=" << p.y << ")"; return stream; } Point2D(double xx = 0.0, double yy = 0.0) : x(xx), y(yy) { } Point2D(const Point2D& point) : x (point.x), y (point.y) { } //Easy setter Point2D& operator()(const double a, const double b) { x = a; y = b; return *this; } double x; double y; }; #endif

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: #ifndef __NODE_H #define __NODE_H #include <mysql++/sql_types.h> #include <iostream> #include "Point2D.h" #include "Toolbox.h" using namespace mysqlpp; using std::ostream; class Trip; struct Point { friend ostream &operator<<(ostream &stream, const Point& p) { // stream << "id:" << p.id // << " dataset:" << p.dataset // << " time:" << p.time; stream << "id:" << p.id << " nr:" << p.nearRoad << " ns:" << p.nearSegment << " sp:" << p.speed; return stream; } Point() { id = 0; nearRoad = 0; nearSegment = 0; nearDistance = 0; heading = -1; time = 0; dataset = 0; speed = 0; coords(0,0); }; Point(sql_bigint_unsigned id, Point2D coords) { this->id = id; nearRoad = 0; nearSegment = 0; nearDistance = 0; heading = -1; time = 0; dataset = 0; speed = 0; this->coords = coords; } double orientation() { return heading >= 0.0 ? Toolbox::mod(heading, 180.0) : -1.0; } //info about near stuff sql_bigint_unsigned nearRoad; unsigned int nearSegment; double nearDistance; double speed; sql_bigint_unsigned time; sql_bigint_unsigned id; int dataset; Trip* trip; double heading; /** -1 = unknown */

Point.h, p1/2

Appendix A - Source Code 69: Point2D coords; 70: 71: }; 72: 73: #endif

Point.h, p2/2

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: #ifndef __POLYGON_H #define __POLYGON_H #include "Point2D.h" #ifndef NULL #define NULL 0 #endif class Polygon { public: Polygon(); /*** Build a polygon around a line at the given spacing */ Polygon(Point2D a, Point2D b, double spacing); virtual Polygon(); void usePoints(Point2D* ps, int num); void dump(); //Accessors int getCount() { return count; } const Point2D* getPoints() { return points; } protected: private: Point2D* points; int count; }; #endif // __POLYGON_H

Polygon.h, p1/1

Appendix A - Source Code 1: #ifndef __PROJECTION_H 2: #define __PROJECTION_H 3: 4: #include <math.h> 5: #include "Point2D.h" 6: 7: class Projection { 8: public: 9: static Point2D convertLLtoNE(Point2D ll); 10: 11: protected: 12: private: 13: /** Semi-major axis */ 14: static const long double a; 15: static const long double invF; 16: // Calculated values 17: /** Semi-minor axis */ 18: static const long double b; 19: /** Flattening */ 20: static const long double f; 21: /** Eccentricity squared */ 22: static const long double e2;// = 2*f - f*f; 23: static const long double e4; 24: static const long double e6; 25: /** Eccentricity */ 26: static const long double e;// = sqrt(e2); 27: /** Second eccentricity squared */ 28: static const long double es2;// = e2 / (1 - e2); 29: /** Second eccentricity */ 30: static const long double es;// = sqrt(es2); 31: /** n */ 32: static const long double n; // = (a-b) / (a+b); 33: static const long double n2;// = n*n; 34: static const long double n3;// = n2*n; 35: static const long double n4;// = n3*n; 36: 37: static const long double g; // = a*(1-n)*(1-n2)*(1+(9*n2)/4+(225*n4)/64)*(M_PI/180); 38: 39: static const double falseEasting; // = 500000.0; 40: static const double falseNorthing; // = 10000000.0; 41: static const long double k0; // = 0.9996; 42: static const double zoneWidth; // = 6.0; //degrees 43: static const double lonWestEdge; // = -186.0; 44: static const double centralMeridianZone0; // = -183.0; 45: }; 46: 47: #endif // __PROJECTION_H

Projection.h, p1/1

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: #ifndef __ROAD_H #define __ROAD_H class Intersection; class Road { public: /** Default constructor */ Road(unsigned int id); /** Default destructor */ virtual Road(); unsigned int getID() { return id; } Intersection* getIntersectionA() { return a; } Intersection* getIntersectionB() { return b; } bool usesIntersection(Intersection* i); protected: private: unsigned int id; Intersection* a; Intersection* b; }; #endif // __ROAD_H

Road.h, p1/1

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: #ifndef __SEGMENT_H #define __SEGMENT_H #include #include #include #include <mysql++/sql_types.h> <math.h> "Point2D.h" "Toolbox.h"

Segment.h, p1/1

using namespace mysqlpp; struct Segment { /** Work out the shortest distance to another point, should use NE not LL */ double distanceTo(const Point2D& p3) { //Ref: http://local.wasp.uwa.edu.au/pbourke/geometry/pointline/ Point2D temp; double dx = stop.x - start.x; double dy = stop.y - start.y; //Check if start == stop, should never happen with our data if (!dx && !dy) return 0.0; double u = ((p3.x - start.x)*dx + (p3.y - start.y)*dy) / (dx*dx + dy*dy); if (u < 0) temp(start.x, start.y); else if (u > 1) temp(stop.x, stop.y); else temp(start.x + u*dx, start.y + u*dy); dx = temp.x-p3.x; dy = temp.y-p3.y; return sqrt(dx*dx+dy*dy); } double orientation() { return heading >= 0.0 ? Toolbox::mod(heading, 180.0) : -1.0; } sql_bigint_unsigned road; unsigned int segment; Point2D start; Point2D stop; double heading; double length; }; #endif

Appendix A - Source Code

Stats.h, p1/1

1: #ifndef __STATS_H 2: #define __STATS_H 3: 4: #include <map> 5: #include <list> 6: 7: #include "LazyTypes.h" 8: 9: using std::map; 10: using std::list; 11: using std::pair; 12: 13: class Stats { 14: public: 15: static Stats* ptr(); 16: static Stats& ref(); 17: virtual Stats(); 18: 19: void addRoadSpeed(ubigint roadID, double speed); 20: double getRoadSpeedGuess(ubigint roadID); 21: 22: void addIntersectionCost(ubigint intersectionID, ubigint fromRoad, ubigint toRoad, double cost, ubigint time); 23: void addRoadCost(ubigint road, double cost, ubigint time); 24: 25: void dump(); 26: void writeToDB(int modelID); 27: 28: protected: 29: private: 30: typedef map< ubigint, list< pair<ubigint,double> > > TimeCostList; 31: typedef pair< ubigint, list< pair<ubigint,double> > > TimeCostEntry; 32: 33: typedef map< ubigint, list< double > > CostList; 34: typedef pair< ubigint, list< double > > CostEntry; 35: 36: /** Costs for a specific a->b route, pair<D,D> is time, speed */ 37: typedef map< pair< ubigint, ubigint>, list< pair<ubigint,double> > > RouteCosts; 38: typedef pair< pair< ubigint, ubigint>, list< pair<ubigint,double> > > RouteEntry; 39: 40: /** Costs for all routes through a particular intersection */ 41: typedef map< ubigint, RouteCosts > IntersectionCosts; 42: typedef pair< ubigint, RouteCosts > IntersectionEntry; 43: 44: 45: Stats(); 46: static Stats* me; 47: 48: /** Speeds along the middle half (25%-75%) of the road */ 49: CostList roadMidSpeeds; 50: IntersectionCosts intersectionCosts; 51: TimeCostList roadCosts; 52: }; 53: 54: #endif // __STATS_H

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: #ifndef __TDSTUPLE_H #define __TDSTUPLE_H #include <ostream> #include "Point2D.h" using std::ostream;

TDSTuple.h, p1/1

/** * Structure for primarily storing Time, Distance and Speed. Other information * is stored for convenience. */ struct TDSTuple { friend ostream &operator<<(ostream &stream, const TDSTuple& o) { stream << o.time << << o.distance << << o.speed << << o.road; return stream; } bool operator==(const TDSTuple& other) const { return ((time == other.time) && (distance == other.distance) && (speed == other.speed) && (road == other.road)); } bool operator!=(const TDSTuple& other) const { return !(*this == other); } TDSTuple(unsigned long time = 0, double distance = 0, double speed = 0, unsigned long road = 0, Point2D location = Point2D(0,0) ) { this->time = time; this->distance = distance; this->speed = speed; this->road = road; this->location = location; } TDSTuple(const TDSTuple& tuple) { time = tuple.time; distance = tuple.distance; speed = tuple.speed; road = tuple.road; } unsigned long time; double distance; double speed; Point2D location; unsigned long road; }; #endif

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: b) { 49: 50: 51: 52: 53: 54: 55: 56: #ifndef __TOOLBOX_H #define __TOOLBOX_H #include "Point2D.h" #include <mysql++/mysql++.h>

Toolbox.h, p1/1

//WARNING! Macros only works correctly for single tokens eg. DEGTORAD(x). It //will not work for multiple tokens such as DEGTORAD(x + y); #define DEGTORAD(deg) ((deg * M_PI) / 180.0) #define RADTODEG(rad) ((180.0 * rad) / M_PI) using mysqlpp::sql_bigint_unsigned; class Toolbox { public: /** Default constructor */ Toolbox(); /** Default destructor */ virtual Toolbox(); /** Double precision modulo function */ static double mod(double value, double modulus); /** Orientation of the line between point A to point B. Assumes * the point stores latitude (y) and logitude (x). We dont care about * direction along the line, just the orientation of the line. * * @return orientation of the two points, >= 0.0, < 180.0 * Ref: http://mathforum.org/library/drmath/view/55417.html */ static double calculateOrientation(Point2D a, Point2D b); /** As for calculateOrientation but in the range [0.0, 360.0] */ static double calculateHeading(Point2D a, Point2D b); /** Similar to heading, but using grid coordinates */ static double calculateDirection(Point2D a, Point2D b); /** Calculate distance between two points */ static double calculateDistance(const Point2D& a, const Point2D& b); /** Return the minimum angle between two orientation angles */ static double minDOrient(double a, double b); static double min(double a, double b) { return a < b ? a : b; } static double max(double a, double b) { return a > b ? a : b; } static sql_bigint_unsigned diff(sql_bigint_unsigned a, sql_bigint_unsigned return (a > b ? a-b : b-a); } protected: private: }; #endif // __TOOLBOX_H

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: #ifndef __TRACE_H #define __TRACE_H #include <list> #include "TDSTuple.h" #include <ostream> using std::list; using std::ostream; class Trace { friend ostream &operator<<(ostream &stream, const Trace& o); public: /** Default constructor */ Trace(); /** Default destructor */ virtual Trace(); void add(TDSTuple tuple); void calculate(); double getMinDist() const; int getPointCount() const { return tds.size(); } ; /** is this trace bad and probably useless to us? */ bool isBad() const; /** Get the total delay from this intersection */ double getDelay() const { return delayA + delayB; } list<TDSTuple> tds; unsigned long getRoadIn() { return inRoad; } unsigned long getRoadOut() { return outRoad; } protected: private: Point2D minBound, maxBound;

Trace.h, p1/1

/** Standard speed along the road inbound to the intersection. Alpha */ double inSpeed; /** Standard speed outbound from the interesection. Beta */ double outSpeed; /** When on this trace the car started to slow. T0*/ TDSTuple slowDownPoint; /** When on this trace the car resumed a normal speed. T1*/ TDSTuple speedUpPoint; /** When this trace was closest to the intersection. Tx */ TDSTuple middlePoint; double maxIn, maxOut; /** Furthest distance in this trace from the intersection on entry */ double minDist; /** Furthest distance in this trace from the intersection on exit */ double maxDist; double delayA; double delayB; unsigned long inRoad, outRoad; }; #endif // __TRACE_H

Appendix A - Source Code 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: #ifndef __TRIP_H #define __TRIP_H #include #include #include #include #include <map> <vector> <list> "Point.h" "Part.h"

Trip.h, p1/1

using namespace std; class Model; /** Continuous path through the model */ class Trip { friend ostream &operator<<(ostream &stream, const Trip& o); public: /** Default constructor */ Trip(Model* m); /** Default destructor */ virtual Trip(); void add(Point*); Point* findNextNonZero(int pointID); void mapToIntersections(Model* m); void write(int id); void walkParts(); void calcCosts(); map<int, Point*> points; map<sql_bigint_unsigned, sql_bigint_unsigned> closestIntersection; map<sql_bigint_unsigned, double> closestDistance; vector<sql_bigint_unsigned> path; list<Part*> parts; void checkPath(); protected: private: Model* model; }; #endif // __TRIP_H

Appendix B

Data Logger User-Guide

101

Using the Data Logger for Andrew Whalan's FYP


Last Revision: 30-Jul-09

Using the Device


The DG-100 data logger you received will already be configured to log at 1 second intervals, and to stop logging if the speed experienced by the device drops below 3 km/hr. The mode selector on the side should remain set to A as the other modes aren't used for the purposes of this project. To turn on the device, hold down the grey button for about 5 seconds. The lights on the front will illuminate briefly and then the device will be operational. To turn the device off, hold down the button for 5 seconds until no lights are illuminated. The light indicators are a bit illogical. The meanings of different are: 1. Satellite Indicator No light: Device is not in data-logging mode or it is off (see power indicator) Solid green: The device is searching for satellites and does NOT have a fix on the location. Data will not be recorded. Flashing Green: the device has a satellite fix and will log data if the speed is >3 km/hr and there is sufficient memory 2. Memory indicator No Light: The device has is between 0% and 60% full. Flashing blue: The device is between 60% and 100% full (Good time to do a download) Solid blue: the device is 100% full, no more data can be recorded. 3. Power Indicator No Light: Device is off Orange light: Device is charging. If the satellite indicator is also on or flashing, the device is operational Red light: The device is powered up. The satellite indicator must be on/flashing for the device to be operational. It's possible for the device to be on, but not operating, which occurs when downloading data. Turning the device off and then on again will cause it to become operational again.

Installing Software Requirements


Installing Serial Driver
The serial driver lets us talk to the actual device. Download the driver from http://www.prolific.com.tw/eng/downloads.asp?ID=31. Extract the zip file and run the installer. This driver should support any windows installation you have up-to and including Windows 7. You'll need administrator privileges to install the device driver.

Installing GPSBabel
GPS Babel is the software lets us do useful things with the device, like download the data from it, and save it in a useful format, which in our case is the GPX format. The standard software that comes with the device is woeful, GPS Babel does a far better job and is far more stable. Download the windows version from http://www.gpsbabel.org/download.html. Again, you'll need to extract the program to a folder. You can put it where you like, there is no installer for it.

Downloading Data
Determining the port assigned to the device
Most computers provide legacy serial ports (COM ports) so the name assigned to the data logger can change. To find out what name has been assigned to it you will need to run Device Manager. There are a number of ways of starting the device manager. One way is: right click on My Computer, select Properties. In the properties window click on hardware and then the Device Manager button. Opening the device manager will show a window similar to the one below. Scroll down and expand the section Ports (COM & LPT) and look for the entry called Prolific USBto-Serial Comm Port (COMx). The COMx will be the port name we need to use in GPS Babel.

Using GPS Babel to download the data


Run GPSBabelGUI.exe to access the device. You will be presented with a screen, you should configure it to look similar to:

You will need to select the appropriate port for the device. Name the file to include the date of download and reference who you are so that I can associate the data with you for analysis. Click Let's Go and the data will be downloaded from the device to the file specified.

Emailing the Data


As the downloaded file can be quite large, and compresses really well, please zip the file first and email the compressed file to: andrew.whalan@studentmail.newcastle.edu.au

How your data will be used


Your data will be used to form an automatically generated, statistical model of the intersections and sections of roads travelled on by all loggers for my final year project. The key to the success of this project is to collect sufficient data to be able to determine statistically significant trends. The best routes to record data for are ones that you travel over frequently, that other people are likely to travel over as well, such as around the university and Newcastle. Your name will not be mentioned in relation to the dataset you provide to preserve your anonymity in the final report. If you have any questions or feedback, please contact me on 0410557166 or via email andrew.whalan@studentmail.newcastle.edu.au

Appendix C

DG-100 Datasheet

107

Key Features

Bibliography
[CM88] Gang-Len Chang and Hani S. Mahmassani. Travel time prediction and departure time adjustment behavior dynamics in a congested trac system. Transportation Research Part B: Methodological, 22(3):217232, June 1988. [F+ 07] Dan Foster et al. GPX 1.1 Standard. TopoGrax, July 2007. http://www.topografix.com/GPX/1/1/. [NW09] Yu (Marco) Nie and Xing Wu. Shortest path problem considering on-time arrival probability. Transportation Research Part B: Methodological, 43(6):597 613, 2009. [PS97] Stefano Pallottino and Maria Grazia Scutella. Shortest path algorithms in transportation models: classical and innovative aspects. Technical Report TR-97-06, University of Pisa, 1997.

[SM75] Peter R Stopher and Arnim H Meyburg. Urban Transportation Modeling and Planning. Lexington Books, 1975. [Sta02] M. Stacy. Converting between grid eastings and northings and ellipsoidal latitude and longitude. Ordnance Survey (UK), September 2002. http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/ convertingcoordinatesEN.pdf.

110

Das könnte Ihnen auch gefallen