ThinkGeo.com    |     Documentation    |     Premium Support

Reverse-geocoding for other non- USA countries?

Hi,


i wonder if MapSuite Web 3.0 has capabilities for reverse-geocoding for people not in the US? (not using GeoCode USA) 


i think one of the must-haves for GIS platforms is the ability to input a Lon Lat coordinate, and a certain method can output its street location, like the nearest intersection, the city/area name...etc. If it has the capability to do this, can someone tell me how?


 


Ricky



 


Ricky
 
You can implement it using the spatial query of a layer. For example, if you want to find a nearest city of “myPoint”, you can write code like


PointShape myPoint = new PointShape(100, 100);
 Collection<Feature> features = citiesLayer.QueryTools.GetFeaturesNearestTo(myPoint, GeographyUnit.DecimalDegree, 1, new string[] { "CityName" });


That returns the closest feature within citiesLayer. Also that feature includes the column info of “CityName”. If you want to get the closest let’s say 3 cities to “myPoint”, just change the variable from 1 to 3.
 
If you want to find which state the point is in, the code will be like:


Collection<Feature> features = statesLayer.QueryTools.GetFeaturesContaining(myPoint, ReturningColumnsType.AllColumns);


That returns a feature within statesLayer contains “myPoint”, with all the columns data it has.
 
It’s similar if you want to find the nearest street to a point. We need to confirm how many streets we want to return as if the point gets the same distance to a couple streets, we might want to return all those streets instead of one. Also if the point is just on an intersection, we properly want to return all the streets which made that intersection. To do this, we can get the closest couple streets first, and check each closest streets to see if the distance to the point is equal. If yes, return all the streets that get the same distance to the point and if no, return only the nearest road. The code will like
 

// return the nearest 4 streets
Collection<Feature> closestStreets = streetsLayer.QueryTools.GetFeaturesNearestTo(myPoint, GeographyUnit.DecimalDegree, 4, new string[] { "StreetName" });

// Loop though to get the closest ones.
foreach (Feature street in closestStreets)
{
double distance = street.GetShape().GetDistanceTo(myPoint, GeographyUnit.DecimalDegree, DistanceUnit.Meter);
// Do what you need with the distance
}



 
Ben.

Ben, 



this code generates an error: Object reference not set to an instance of an object.


Collection<feature> features = citiesLayer.QueryTools.GetFeaturesNearestTo(myPoint, GeographyUnit.DecimalDegree, 1, new string[] { "CityName" }); 

so i try to initialize first, but still the same error: 


Collection<Feature> features = new Collection<Feature>();
features= RoadsLayer.QueryTools.GetFeaturesNearestTo(p, GeographyUnit.DecimalDegree, 1, new string[] { "ST_NAME" });



 


my RoadsLayer is a ShapeFileFeatureLayer, dont know why it cause error... 

 



Ric,


I think that’s because the layer is not opened so layer.QueryTools is null. Please double check the layer.Open() is called before that “QueryTools” statement. If you still have problem, please have a look the attached sample for it.(The data is not attached because of the size limit)


Ben.


 


 



266-post4815.zip (98.8 KB)

Yes Ben, it has to be open first. i converted the CustomOverlay to FeatureLayer, then it can query the features:






FeatureLayer StreetsLayer = (FeatureLayer)((LayerOverlay)Map1.CustomOverlays["MyBaseOverlay"]).Layers["ExpresswayLabelLayer"]; 

StreetsLayer.Open(); 
Collection closestStreets = StreetsLayer.QueryTools.GetFeaturesNearestTo(myPoint, GeographyUnit.DecimalDegree, 5, new string[] { "ST_NAME" }); 
foreach (Feature street in closestStreets) 
   { 
    double distance = street.GetShape().GetDistanceTo(myPoint, GeographyUnit.DecimalDegree, DistanceUnit.Meter); 
    tbLabel.Text += "Closest street: " + street.Id + " Distance: " + distance.ToString(); 
   }
StreetsLayer.Close(); 


 The problem is, what "street.Id" returns is a number like "103992", not the name of the street i was expecting. The names of the streets are contained in the .dbf file which has the same file name as .shp, under the column "ST_NAME".  How do i extract the names of the streets?



Ric, 
  
 Street.Id is just an Id for that feature, the name of the street can be get by the ColumnValues property on the “street” object. Codes should be like this. 
  
 
foreach (Feature street in closestStreets) 

    double distance = street.GetShape().GetDistanceTo(myPoint, GeographyUnit.DecimalDegree, DistanceUnit.Meter); 
    tbLabel.Text += "Closest street: " + street.ColumnValues["ST_NAME"] + " Distance: " + distance.ToString(); 
}
 
  
 Ben.

It worked. Thanks Ben.

Always my pleasure:)

Ben, i have a follow-up question. The “distance” from this query: 
  
 double distance = street.GetShape().GetDistanceTo(myPoint, GeographyUnit.DecimalDegree, DistanceUnit.Meter);  
  
 is in meters. What is it? thats the diatance from myPoint to the start of a feature (street), to the end of the street, or a perpendicular line…etc. i tried this “distance” value from many different points, i still can’t figure out what its meaning. 
  
 Ric

Ric, 
  
   The distance from the point to the shape is the shortest distance.  This means in a straight line from the point to whatever shape, if the shape is a line then it is to any point on the line.  In a simple case image that you have a two point  line running from left to right.  Then you place a point to measure from around the middle of the line but slightly above it.  The distance measured would be to the center of the line.  It will give you the distance to any point on the line not just the two points that make up the line.  It sounds confusing but I hope I shed some light on it. 
  
 If you want to see this graphicly you can go to the link below.  Check out under the Features node and pick the Shortest Line Between features, this is the kind of distance you should get. 
  
 websamples.thinkgeo.com/webeditionsamples/ 
  
 David

Thanks David, i got it. Since my map data has no detailed street household numbers, as of now i can only determine the nearest street that a point is at. How can i determine the nearest intersection of a point? Given a point, i should be able to say something like "myPoint is x meters from intersection of A street and B street…"? 
  
 Ric

Ric, 
    
   On the street data you have does it have any address infromation like the street start house number and the stop one?  Curious as if you know that you can extrapolate the number based on where the point intersects the line. 
  
 David

Ric, 
  
   The intersections is a bit trickier.  This might not be the best way but I will take a stab at it.  
  
   I would start with your spatial query to find the X number of nearest streets from the point. You could do the nearest neighbor or you could make a circle of X radius around the point and do a spatial query for streets that overlap that.  After you have a collection of streets I would loop through each line, inside of that next loop though each point that makes up the line.  For each point then I would loop through all of the other lines and their points.  You need to make sure you ignore streets with the same name as the street you are currently on.  What you are looking for is a case where the points are equal but the street name is different.  If the data was digitized correctly then intersection are lines where the two streets share the exact same point in common.  After you find an intersection then put in some sort of intersections collection, keeping the point in common and the two street names.  Next keep looping through the code above until you have found all of the possible intersections.  At that point you loop through your intersections results to find the point that is closest to your starting point. 
  
 I know there is a lot of subtle things that need to be worked out but that is in in a nutshell without coding it up.  I hope this helps.  I am also going to put a issue into our system to consider making a helper class to do this kind of thing.  I think it would be useful to others.  I cannot say though when it would be implemented as we are in the middle of many new releases. 
  
 David

Hi,  
 I am not sure if I understand this correctly… 
 so we can find the nearest features (eg. cities, streets ect) to a point provided by us? 
  
 … if I don’t know about the point axises … but the city name (e.g. Vancouver) … If I want to show the city when the map starts up,  i should get the bounding box of this city and set the extent with it?  Am I on the right track?  Is there a way to find the bounding box of a specific place? 
  
 Thanks! 
   
  
 Roson 
  
 Roson

Roson,



In Web Edition, there are several ways to initialize the extent when the map starts up. Here are three methods to do it, please see my comments below.// Set current extent;
// If you have a featureLayer which you want to display when the map start up,
// featureLayer.GetBoundingBox() is the specified place you want.
Map1.CurrentExtent = featureLayer.GetBoundingBox();

// Set current center and scale.
Map1.ZoomTo(centerPoint, targetScale);

// Keep current center and zoom to the specified scale
Map1.ZoomToScale(targetScale);



Please let me know if you have any questions.



Thanks,

Howard



Hi Howard, 
  
 I have a shape file that contains map data/feature layer to the city level.  For each feature (polygon of cities), there are columns of polygon id, polygon name etc.  So if given a city name, how can I get the bounding box of it on the client side?    
  
 I have added the feature layer to the map as the following in page_load method: 
                
                ShapeFileFeatureLayer cityLayer = new ShapeFileFeatureLayer(MapPath("App_Data/Adminbndy4ca00.shp"));
                cityLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.CreateSimpleAreaStyle(GeoColor.FromArgb(255, 243, 239, 228),       GeoColor.FromArgb(255, 218, 193, 163), 1);
                cityLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
                cityLayer.Name = "CityLayer";
              

                // Overlay for map data
                LayerOverlay mapOverlay = new LayerOverlay("CustomOverlay");
                mapOverlay.IsBaseOverlay = true;
                mapOverlay.Layers.Add(cityLayer);
               
 
  
 Then, I think i should get the customoverlay on the client side… 
 <code lang="script"> var customOverlay = Map1.GetOpenLayersMap().getLayer("CustomOverlay"); </code> 
  
 How do I get the polygon/feature (ie. city) from there?  is there a way to do that? 
 Then after that… I shall be able to do  
 <code lang="script"> 
  //the the feature would be the polygon of the city? 
  Map1.CurrentExtent = feature.GetBoundingBox(); </code>  
 ? 
  
 thanks a lot! 
 Roson 
  
  
  


Hi Roson,


I think you need to use a callback to implement your requirement, because we can not get a specific feature by a column value in the client side. 
We have done a sample to demonstrate how to do this, please take a look. The sample is just using a country shape file, find a country’s bounding box by its name and return to the client side by callback. 
If you want to run this sample, you need to add the reference of WebEdition.dll and MapSuiteCore.dll, and put the “Countries02” shape file to the “App_Data” folder.
Any more questions please let me know.
Thanks,
Sun

1361-Post4815Demo.zip (178 KB)

Hi Sun, 
  
 When I tried to run this application… it threw me error "Error 1 It is an error to use a section registered as allowDefinition=‘MachineToApplication’ beyond application level.  This error can be caused by a virtual directory not being configured as an application in IIS. C:\Documents and Settings\lluo\My Documents\Downloads\post4815\post4815\post4815\Web.config 39  
 ".  Does it have something to do with ViewState?  … How could I fix this and run it? 
  
 thanks! 
  
 Roson

Another thing is that I need to use CustomOverlays to add SimpleMarkerOverlay s… I won’t be able to use StaticOverlay or DynamicOverlay as you do in your sample…  
  
  FeatureLayer worldLayer = (FeatureLayer)Map1.StaticOverlay.Layers[“WorldLayer”];
            InMemoryFeatureLayer highlightLayer = (InMemoryFeatureLayer)Map1.DynamicOverlay.Layers[“HighlightLayer”];

            worldLayer.Open();
            Collection<Feature> features = worldLayer.QueryTools.GetFeaturesNearestTo(pointShape, GeographyUnit.DecimalDegree, 2, new string[] { “STATE_NAME” });
            worldLayer.Close(); 
 
  
 I added my ShapeFileFeatureLayer to the map with CustomOverlays… Is there a similar way to do the feature query with CustomOverlays?  
  
 Thanks! 
  
 Roson 


Hi Roson,


That is strange, I have checked my sample, and it works fine in my machine. Another thing is that I don’t use any StaticOverlay or DynamicOverlay in my sample, and the code you pasted on this post doesn’t exist in the sample either. So I will paste some of my code here and upload the sample again, please take a look. To use the sample, you just need to unzip it and open the solution called “Post4815”, and then debug it. 
I just use a call back to do this, and the C# code is like this:


public string GetCallbackResult()
{
return callbackResult;
}

public void RaiseCallbackEvent(string eventArgument)
{
string countryName = eventArgument.Trim();
LayerOverlay overlay = Map1.CustomOverlays[0] as LayerOverlay;

if (overlay != null)
{
ShapeFileFeatureLayer layer = overlay.Layers[0] as ShapeFileFeatureLayer;
if (layer != null)
{
if (!layer.IsOpen)
{
layer.Open();
}
Collection<Feature> returnFeatures = layer.FeatureSource.GetFeaturesByColumnValue("CNTRY_NAME", countryName);
if (returnFeatures.Count > 0)
{
callbackResult = returnFeatures[0].GetBoundingBox().ToString();
}
if (layer.IsOpen)
{
layer.Close();
}
}
}
}


Hope this makes sense.
Any more questions please let me know.
Thanks,
Sun

1368-Post4815.zip (177 KB)