ThinkGeo.com    |     Documentation    |     Premium Support

POIs in Route

Hi,


I want to generate a route, and, along the path generated, identify and plot POIs on which the road goes by. 


Is it possible to put those POIs in a separate .shp file  (different from the road .shp) and have the Rounting Add-on identifying those points for me after it build the route?


Thanks



I think that Map Suite Routing Extension will meet your needs for that but I would need some more concrete explanations on what you want to accomplish exactly.  
  For the first question "Is it possible to put those POIs in a separate .shp file?". The answer is" Yes, of course. You can put POIs in any format you want. But I am trying to understand exactly what your scenario is. Do you want to plot yourself the POIs on a route after generating it, or do you want to have a route passing thru some specific POIs? Can you explain in a little more detail what your case is? thank you.

Val, 
  
 I want to plot those POIs on a route after generating it. For instance, let’s suppose I have a .shp of gas stations or a .shp of toll stations, after the route being calculated and plotted, I want to put those POIs on the map. But I don’t want to add the entire layer on the map, I just want to put those POIs that are in the route path. How can I do that? 
  
 Besides that, I need to include those POIs in the route description, in order to have something similar to the following in the end: 
  
 Road A for 3.3 mi 
 Road B for 2 mi 
 Toll Booth (Fare $5) 
 Road C for 1 mi 
 Road D for 4 mi 
 Shell Gas Station 
 Road E for 1000 mi 
 (…) 
  
 Did I make myself clear in my explication now?

Ok, I think I understand what you want to do with the POIs. Once the route is generated from start point to end point, you want to plot on the map, let's say, all the gas stations on that route, the gas stations being in a shapefile. To do that I would do a spatial query on the gas station shapefile using the MultiLineShape from the result of the route. I get the gas stations within a certain distance (tolerance) from the route. I chose 100 meters. The result is a collection of Features and I use an InMemoryFeatureLayer to display separetly (in red in my example). (I use a fictional shapefile of gas stations for this illustration). See the screen shot below:



 You can see below the code I used:


 



// Set the Map Unit and current extent
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.StandardColors.LightGoldenrodYellow);

            // Defines a new layer to render the Austin streets
            ShapeFileFeatureLayer StreetLayer = new ShapeFileFeatureLayer(@"..\..\Data\Routing\austinstreets.shp");
            StreetLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = LineStyles.LocalRoad3;
            StreetLayer.ZoomLevelSet.ZoomLevel01.DefaultTextStyle = TextStyles.CreateSimpleTextStyle("Fename", "Arial", 10, DrawingFontStyles.Regular, GeoColor.StandardColors.Black);
            StreetLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            //defines layer for gas stations
            ShapeFileFeatureLayer gasStationLayer = new ShapeFileFeatureLayer(@"..\..\Data\Routing\gasstations.shp");
            gasStationLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = PointStyles.CreateSimpleSquareStyle(GeoColor.StandardColors.Gray, 12);
            gasStationLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            LayerOverlay StreetOverlay = new LayerOverlay();
            StreetOverlay.Layers.Add("AustinStreetsLayer", StreetLayer);
            StreetOverlay.Layers.Add("AustinGasStationLayer", gasStationLayer);
            winformsMap1.Overlays.Add("AustinStreetOverlay", StreetOverlay);

          
            winformsMap1.CurrentExtent = new RectangleShape(-97.7532,30.3012,-97.7244,30.2802);  
          

            // Define a Routing layer to render the route and stops
            RoutingLayer routingLayer = new RoutingLayer();
            StreetLayer.Open();

            Feature feature1 = StreetLayer.FeatureSource.GetFeatureById("5156", ReturningColumnsType.NoColumns);
            Feature feature2 = StreetLayer.FeatureSource.GetFeatureById("8334", ReturningColumnsType.NoColumns);

            routingLayer.StartPoint = feature1.GetShape().GetCenterPoint();
            routingLayer.EndPoint = feature2.GetShape().GetCenterPoint();
            StreetLayer.Close();

            LayerOverlay routingOverlay = new LayerOverlay();
            routingOverlay.Layers.Add("RoutingLayer", routingLayer);

            winformsMap1.Overlays.Add("RoutingOverlay", routingOverlay);

            RtgRoutingSource rtgRoutingSource = new RtgRoutingSource(@"..\..\Data\Routing\austinstreets.rtg");
            RoutingEngine routingEngine = new RoutingEngine(rtgRoutingSource, StreetLayer.FeatureSource);

            //InMemoryFeatureLayer for the gas stations on the route (within tolerance)
            InMemoryFeatureLayer inMemoryFeatureLayer = new InMemoryFeatureLayer();
            inMemoryFeatureLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = PointStyles.CreateSimpleSquareStyle(GeoColor.StandardColors.OrangeRed, 12);
            inMemoryFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            StreetOverlay.Layers.Add("SelectedAustinGasStationLayer", inMemoryFeatureLayer);

            winformsMap1.Overlays["RoutingOverlay"].Lock.EnterWriteLock();
            try
            {
                routingLayer.Routes.Clear();
                MultilineShape routeMultiLineShape = routingEngine.GetRoute(routingLayer.StartPoint, routingLayer.EndPoint).Route;
                routingLayer.Routes.Add(routeMultiLineShape);

                gasStationLayer.Open();
                Collection<Feature> features = gasStationLayer.QueryTools.GetFeaturesWithinDistanceOf(routeMultiLineShape, winformsMap1.MapUnit, DistanceUnit.Meter,
                                                100, ReturningColumnsType.NoColumns);
                gasStationLayer.Close();

                inMemoryFeatureLayer.Open();
                inMemoryFeatureLayer.EditTools.BeginTransaction();

                foreach (Feature feature in features)
                {
                    inMemoryFeatureLayer.EditTools.Add(feature);
                }
                inMemoryFeatureLayer.EditTools.CommitTransaction();
                inMemoryFeatureLayer.Close();

            }
            finally
            {
                winformsMap1.Overlays["RoutingOverlay"].Lock.ExitWriteLock();
            }

            winformsMap1.Refresh();
 

 


 For your last question, let me think about that and I will give you an answer later.



Regarding your second question on including the POIs in the route description, it is possible. In the RoutingResult from RountingEngine.GetRoute, you get a collection of RouteSegment and Feature. Using that data, you can query some external POI (by doing some spatial queries and other operations) to be included in the route description. In the sample "Routing Directions" of the "How Do I" apps, there is a function ShowTurnByTurnDirection. You could basically extend this function to have an external point based layer included. If you need some more concrete steps, let us know and we can work on a Code Community project showing that. Thank you.


 I think that you saw that we published a project related to your first question in the Code Community:


code.thinkgeo.com/projects/show/poionroute


 



Thanks for you solution a long time ago Val. 
  
 In fact, for some data, I need something slightly different.  
 I want to identify the POIs that are on a specific side of the road, considering the route direction. This is very useful for identifying POIs like gas stations, or toll both, on a road (I want to show those that are on the right side while I’m driving). 
 The image bellow shows which POI should be selected considering what was written. Is it difficult to implement? I think it’s very useful for a routing platform. I hope that I explained everything clearly.  
  
 Have a great day.

Sorry, the image is here:




Thank you for your interesting request, Gustavo. I think that this should be possible without too much difficulties. I am currently taking a couple of days off. I will work on a modification of the project with your request later this week. I will let you know the status of the progress on that.

Testing GetFeaturesWithinDistanceOf in my real app I got really worried. 
  
 For short routes, say 250km, it works great, but for little longer routes, it  becomes really slow, until a point in which a timeout exception is thrown -  a ContextSwitchDeadlock exception. Is this operation expensive in cpu cycles?

Gustavo, 
  
   I have an idea why this could take all of of time.  The reason is that is you have a very long route then the rectangle that is generated by that route could  be very large.  We use the bounding box rectangle to do a spatial query to find all the points then we check each point returned to see if it is within the distance.  If you route is long then the bounding box could be huge so this will cause us to check many points that are not really candidates.  I suggest that if you only check for POI a short distance from the road that you use the method but not passing in the whole route but segments at a time.  In this way each segment will be short and the calculation fast.  This will cause less points to be checked.  I will see if we can work up a sample to show this.  In the mean time try to use the method one ID at a time and add the results to a dictionary or something as you need to make sure that points are not entered twice as they may be queried twice. 
  
 David

Thanks David…  
  
 That’s is probably what is happening. I’ll try to query for POIs for each route segment individually, and add the POIs to a set. I hope that a lot of segments won’t cause another problem of having to query for POIs too many times.  
  
 Is this bouding box method fast?

Gustavo, 
  
   The query of the points should be pretty fast even if you do it over and over.  That query will use the R-Tree spatial index which is fast.  This way you will end up querying far fewer points along with their data.  I would image if you have lots of POI then this way should be faster and use less resources.  Let me know how it goes or if you need any help.   
  
 David

Gustavo, 
  
   One more thing you could do is to query the POI but do not specify to bring back any column information.  That way points that initialy get queried because they are in the bounding box but not within the distance do not have the overhead of fetching their dbf data.  Once you get the result then go back and get the column values for the ones you are interested in.  Or maybe just get the least amount of information possible.  I only mention this because many times for points the actual point data is just 16 bytes, x & y as double, and the column data in the DBF can be much more and might be most of the overhead in reading from the disk.  
  
 David

The performance is a lot better now David. It works great for a small POIs shapefile (for toll both) that I tested. 
  
 Still, I think I’m missing something for the another purpose that I have for this idea. I’d like to identify the cities from which the route passes through in the route description. And this route can be really long (50.000 km, for instance). So, what is you thought about it? Should I work on my road shape (breaking segments, according to the city each one belongs) and put the city information there, or are there any other (fast) possibilities? 
  
 Thank you 


 


Gustavo,
I have another idea about it. Maybe you can try FeatureSource.GetFeaturesIntersects if the city data is stored in a single file. We can create a city featuresource, and then do the intersection with the route, the return features are the cities that the route passes through.
Thanks,
Johnny

Gustavo,


 Regarding your first request to find the points on the right side of the route, we created a new project in the Code Community "POI on route (advanced)", please, check it out: code.thinkgeo.com/projects/show/poionrouteadv


 



I’ve tried the example approach but I’m getting some trouble with the tolerance parameter. It throws exception depending on the route. What’s its meaning?  
  
 What if I want to determine the POI side while looping through the RouteSegments collection (thus getting the MultilineShape of the feature in the RouteSegment)? What do I have to modify?  
 I tried this out too, but the FindSide method doesn’t give the right result. I didn’t use the MultilineShape’s Reorder method in this case though.

I am not sure I understand your scenario. Could you explain it more concretely giving us some specific examples? A sample app you could send us and that we can run will be the best for us to help you.

Hi Val, 
  
 I need to perform something like: 
  
 
                    foreach (RouteSegment routeSegment in routingResult.RouteSegments)
                    {
                        Feature feature = featureSource.GetFeatureById(routeSegment.FeatureId.Split(new string[] { “::” }, StringSplitOptions.RemoveEmptyEntries)[0], new String[] { });

                        // perform a buffer near the feature for POIs

                        // filter the POIs based on its side of the road, getting just those that are on the right side when driving
                    }
 
 
  
 I know how to perform the buffer and so forth. I just need to know how to filter the fetched POIs based on their position relative to the road. I would like to perform that on a RouteSegment by RoutSegment fashion.  
  
 I had tried an approach similar to the last sample code you provided, but sometimes it threw an exception due to a wrong tolerance parameter (I tried 5, and some others). That’s why I think a RouteSegment by RoutSegment approach would more reliable. 
  
 Thank you

I don’t understand why you are diverting from the logic used in the Code Community project for basically performing the same task with your data. The logic you see in the Code Community project was carefully put into place performing operations such Reorder of the result MultiLineShape etc. Why do you want to use a different logic? If there is a tolerance problem, we need to examine why it throws an exception with your data. Can you please send us a little sample app with your data? And then, I think, I will have a better understanding of what is going on on your side. If you don’t feel comfortable attaching your sample app to this Discussion forum, please, send it to support@thinkgeo.com. Thank you.