PDA

View Full Version : Driving time between LatLong & street address



wibeasley
02-18-2012, 03:58 PM
I want the driving distance/time between a street address (eg, public libraries) and a location designed by its LatLong (eg, Census Tract or Block centroid). I'm using MapPoint 2011 and C# 4.

I assume the 'MapPoint.Route' class is what I want. I'd like to avoid reverse geocoding every Census Block. Is there a way to find a driving distance directly from the LatLong? If not, what reverse geocoding algorithms do people like now? I see several on http://www.mp2kmag.com (http://www.mp2kmag.com/) but they are around 10 years old. Are those still the best for this?

Eric Frost
02-18-2012, 05:24 PM
The old articles should still apply.

The method to create a Location object from lat/lon is simply objMap.GetLocation(), hope that helps. I think it takes a little time to learn the MapPoint API, hopefully this forum is a good resource for you!

Eric

Eric Frost
02-18-2012, 05:29 PM
Oh and Welcome to the Forums! How long have you been programming with MapPoint? What versions?

Thanks,
Eric

wibeasley
02-18-2012, 09:41 PM
That was very helpful, thank you Eric. I had seen that Map.GetLocation method on this thread (http://www.mapforums.com/round-tripping-street-address-latlong-6385.html). But my implementation was acting funny, so I went to another approach. Your advice returned my attention to it, and now I directly add the Location object as a waypoint (instead of transforming it first to a FindResults object). It seems to be working well. I realized I have those addresses already geocoded, so I'm avoiding street addresses on both ends of the route.

To answer your question, I've been using MapPoint for a few days. You're right, the API is taking me time to learn. But it's the API itself isn't my only barrier. The COM interface doesn't help and I'm not crazy about the documentation. I was doing everything in R (with this approach: http://www.amazon.com/Applied-Spatial-Data-Analysis-Use/dp/0387781706/), but our study has outgrown the great circle distance and needs driving duration. I miss being able run things on the University's supercomputer.

Is it wishful thinking that the API is now thread safe? Are people's recommended approaches usually similar to this: c# - Multi-threading access to MapPoint? - Stack Overflow (http://stackoverflow.com/questions/4314458/multi-threading-access-to-mappoint) ?

Eric Frost
02-19-2012, 11:11 AM
Pretty interesting. That looks like a good book. Looks like one of the book I get excited about and buy and then sits around unread for years :-)

Richard Marsden is the resident expert on MapPoint and threading.

Eric

Winwaed
02-20-2012, 08:52 AM
Yeah, I see my answer only got one vote to that StackOverflow thread, even though it was the only one that referred to MapPoint!

Some have reported it is thread safe but I think they must be doing some very specific managed stuff. I find it is not, and all operations to a MapPoint instance should ALL work through the same thread - the thread that created the instance.

Multiple MapPoint instances have also been known to interfere - e.g. two MapPoint instances cannot load the same file at the same time. You will need to implement a lock so that one thread waits for the firs tthread to finish loading a file before it starts itself. I did not investigate too closely - it might be the file load mechanism that is at fault, but I suspect it is the individual file access/locking. (ie. loading two different files should be okay)

wibeasley
02-21-2012, 12:08 PM
Thanks guys. Your Sept 2010 MSDN magazine article (http://msdn.microsoft.com/en-us/magazine/ff959816.aspx) was one of the first things I read when I started using MapPoint last week, and it was one of the things that prompted me to try it. Right now, I'm trying to eliminate as much waste as possible from my code. If I can't get it significantly faster, I think I'll have to go to something that runs on the supercomputer, which eliminates any Microsoft product. With the exception of scaling, this thing is working well enough, so I'd hate to abandon it.

All I really need is driving time. I don't need any (a) directions, (b) user interaction, or (c) visual maps. I've included the core parts of my code below. Is there any other feature that I can turn off?

When the 'CalculateTime' class is initialized, I deactivate the following things.

_app = (MapPoint.Application)Activator.CreateInstance(Typ e.GetTypeFromProgID("mappoint.application"));//http://stackoverflow.com/questions/4862319/
_app.Visible = false;
_app.UserControl = false
_app.ShowNavigation = false;

_map = _app.ActiveMap;
_route = _map.ActiveRoute;

Inside the main loop is this: ('block' refers to Census Block; 'resource' refers to something like a library, pediatrician, or support group).

_blockLocation = _map.GetLocation(block.Latitude, block.Longitude, 0);
_resourceLocation = _map.GetLocation(resource.Latitude, resource.Longitude, 0);
_route.Waypoints.Add(_blockLocation);
_route.Waypoints.Add(_resourceLocation);

_route.Calculate();
double drivingDistanceInMiles = _route.Distance;
double drivingTimeInDays = _route.DrivingTime; //http://msdn.microsoft.com/en-us/library/aa723692.aspx
Int32 drivingTimeInMinutes = Convert.ToInt32(drivingTimeInDays * 24 * 60);

return new DriveLength(distanceInMiles: drivingDistanceInMiles, durationInMinutes: drivingTimeInMinutes);

When the class is disposed, these things are released:

_map.Saved = true;
Marshal.ReleaseComObject(_route); //http://stackoverflow.com/questions/4862319/
Marshal.ReleaseComObject(_map);
Marshal.ReleaseComObject(_blockLocation);
Marshal.ReleaseComObject(_resourceLocation);

((MapPoint._Application)_app).Quit(); //http://www.dotnet247.com/247reference/msgs/9/45708.aspx
Marshal.ReleaseComObject(_app);

Is there anything that's being calculated or determined that's unnecessary for driving time? I assume driving distance isn't adding to the cost.

Richard, I followed your stackoverflow post. I dispose and reinitialize the class (and therefore the MapPoint COM object) for every 'resource'. That turns out to be about every 60,000 routes (or every few hours).

I don't need a super accurate driving time. Is there a way I can ask for a quick approximation, instead of a route that's very likely to be optimal? Having less accuracy isn't ideal, but I need to start using some approximations somewhere to be feasible. I'd rather approximate the driving time (especially if the code is a lot faster), than use units with less spatial resolution (like Census Block Groups or Tracts).

I've done a little modeling, using Haversine miles(x) to predict drive minutes (y). When x < 15, a decent prediction is y = 2.721 + 2.245*x + -.07659*x*x + .002020*x*x*x. This eliminates about 20% of the routes I need to send to MapPoint. I imagine this approximation wouldn't work for states with mountains or jagged coastlines, but it works for Oklahoma so far. The errors are usually less than 5 minutes, which is acceptable, because that's less time than it takes to get a kid dressed and in and out of a car seat.

I appreciate the direction this forum's given me so far, with this and other threads. My background is stats. If a new user has a stats-related question in the future, PM me and I'll do my best to help them out in the same way.

Eric Frost
02-21-2012, 12:16 PM
I dispose and reinitialize the class (and therefore the MapPoint COM object) for every 'resource'. That turns out to be about every 60,000 routes (or every few hours).

I wonder if MapPoint memory usage grows and it slows down significantly before you dispose and reinitialize the class. Maybe put in something to log the time it takes for each route? You might see in the first hour it is taking say 2 seconds per route, but after 40,000 or so routes it has slowed down to 10 or 20 seconds per route.

You could look at your log to see if there's an optimal # of records it gets through before you should reinitialize MapPoint.

Does that make sense?

Obviously if it's still doing the routes just as fast at the end as it was at the beginning i.e. it's not slowing down, then the suggestion is moot, but it's probably worth checking if you haven't already.

hope this helps,
Eric

wibeasley
02-21-2012, 01:10 PM
Maybe put in something to log the time it takes for each route? You might see in the first hour it is taking say 2 seconds per route, but after 40,000 or so routes it has slowed down to 10 or 20 seconds per route.I've done that casually, which lead me to every few hours. At 12 hours, I noticed it severely bogging.

I'd like to postpone tuning the ad-hoc GC, while the inner loop is still changing. I was hoping first someone would detect a few obvious inefficiencies. No problems popped out to you? Any features I could disable or things I could do differently? Is there a way to ask for a quick & dirty drive time?

(I just made one little change. The "
_resourceLocation = _map.GetLocation(resource.Latitude, resource.Longitude, 0);
" line is set only at the beginning of the class, and not in the loop for all 270,000 Census blocks.)

wibeasley
02-21-2012, 04:06 PM
If this helps anyone in future projects: I polled the MapPoint duration every 1,000 calculations. Most responses were under 200ms on a Core2 Q9550 with 8GB RAM, & Windows Vista. The vertical orange line shows where the COM object was disposed and new one initialized; this happened after about 2.5 hours of calculations. (It started almost immediately after Eric's previous post). And according to the Task Manager, the memory doesn't get bloated; MapPoint.exe's private working memory remains under 300MB.

Unless there's some devious confound between (a) route complexity and (b) order of calculation, MapPoint 2011 doesn't seem to bog down in this scenario with two WayPoints.

Eric Frost
02-21-2012, 06:25 PM
Great work, yeah I thought maybe a stat guy wouldn't pass up the idea of measuring something :-)

http://www.mapforums.com/attachments/684d1329861950-driving-time-between-latlong-street-address-rplot01.png

Eric Frost
02-21-2012, 06:29 PM
(I just made one little change. The "
_resourceLocation = _map.GetLocation(resource.Latitude, resource.Longitude, 0);
" line is set only at the beginning of the class, and not in the loop for all 270,000 Census blocks.)

Good catch.
One thought, I don't know where you are getting the coordinates e.g. block.Latitude/resource.Latitude from or writing out the results, but there might be a little overhead?
If you compile everything into a MapPoint COM Add-in including the arrays or dictionaries of data, it might go faster?
It is said that MapPoint COM Add-ins are faster than automating the MapPoint Application object externally, though I haven't done any testing myself.
Eric

wibeasley
02-21-2012, 09:07 PM
As far as the routine is aware, the LatLong come from (and the DrivingTimes go to) a strongly-typed ADO.NET DataSet. Ultimately it connects to a Sql Server 2008 R2 Enterprise database; those two computers go hours without talking to each other, and persisting the records takes less than 10 seconds. So unless I'm overlooking something, unfortunately there's little potential gain here. Before this routine executes, the coordinates have already been geocoded from street addresses in MapPoint 2011.

Until your post Eric, I wasn't aware of MapPoint COM Add-ins. I see the O'Reilly (http://shop.oreilly.com/product/9780596009069.do) book has 8 pages on it (p133-140). And I also see two (http://www.mp2kmag.com/a146--com.addin.mappoint.html) mp2kmag (http://www.mp2kmag.com/a70--graticule.com.add-in.mappoint.html) articles. It will be a few days before I can return to this project. Is anyone aware of other good sources for MapPoint Add-ins that I should read when I resume?

And yeah! One small part of the project has completed as I'm writing this. Calculating the distances for 7 support group locations took about a day; 11 million routes were calculated (out of a possible 7*270,000). Unfortunately some resources, like pediatrician & GP offices, number in the thousands. At this pace, it would take over a year to calculate those distances. This isn't an option.