MapPoint Web Service has a certain set of methods that find entities using their identities and properties, but these methods can only be used with the custom data uploaded to the MapPoint servers. These methods are particularly useful for queries that depend on nonspatial attributes. For example, if you upload all your ATMs to MapPoint servers and you want to display all ATMs in the city of Chicago, or only the ATM that has the unique identity of 13324, these methods can be either simple non-spatial queries or spatial queries. In this section, let’s look at these find methods that can be used with your custom data.
Find entity by identity
You can use the FindServiceSoap.FindByID method to find entities using their entity IDs. Like any other find method, this method takes a specification object of type FindByIDSpecification and returns a FindResultsobject. TheFindByIDSpecificationobject takes up to 500 IDs as input parameters. Table 6-6 shows the fields exposed on theFindByIDSpecificationobject.
Table 6-6. Fields exposed in the FindByIDSpecification object
Field
Description
DataSourceName
Data source name as a string
EntityIDs
Array of unique entity IDs; only points of interest with matching entity IDs are returned, while the rest are ignored
Filter
The filter (FindFilterobject) to apply to the results, which includes the specific entity type, properties, and values that the returned results must match
Options
The search options (FindOptionsobject), which may include the range of results and a flag to identify which objects are desired in the returned results
The following code shows how to use theFindByIDmethod:
//Create a Find Service proxy FindServiceSoap findService = new FindServiceSoap(); //Assign credentials . . .
//Define find by id specification FindByIDSpecification findbyidspec = new FindByIDSpecification();
//Assign a data source name findbyidspec.DataSourceName = "MapPoint.FourthCoffeeSample";
//Apply a filter for entity name findbyidspec.Filter = new FindFilter(); findbyidspec.Filter.EntityTypeName = "FourthCoffeeShops";
//Now assign the entity IDs to find int[] arrayID = {-21835, -21836}; findbyidspec.EntityIDs = arrayID;
The found entities are returned in the same order that the entity IDs are passed in, but you can override this sorting behavior using theFindFilter.SortProperties. Assuming that you want to sort the ATMs in the previousFindByIDmethod by their associated bank name (assuming that there is a property calledParentBankName), the method call looks as follows:
//Create a Find Service proxy FindServiceSoap findService = new FindServiceSoap(); //Assign credentials . . .
//Define find by id specification FindByIDSpecification findbyidspec = new FindByIDSpecification();
//Assign a data source name findbyidspec.DataSourceName = "MapPoint.FourthCoffeeSample";
//Apply a filter for entity name findbyidspec.Filter = new FindFilter(); findbyidspec.Filter.EntityTypeName = "FourthCoffeeShops";
//Specify what properties to be used to sort the found results SortProperty[] sortproperties = new SortProperty[1]; sortproperties[0] = new SortProperty(); //Assign the property name to be sorted on sortproperties[0].PropertyName = "ParentBankName"; //Specify the sort direction: Ascending or Descending sortproperties[0].Direction = SortDirection.Descending;
//Assign sort specification to the find filter findbyidspec.Filter.SortProperties = sortproperties;
//Now assign the entity ids to find int[] arrayID = {-21835, -21836}; findbyidspec.EntityIDs = arrayID;
As you can see, theSortPropertiesmethod is an array, so you can sort the resulting entities by more than one attribute if needed.
It is important to remember that the points of interest entity identities are not persisted from one version of the MapPoint Web Service to another, so if you hardcode the point of interest entity IDs into your application, when you upgrade to a newer MapPoint Web Service, your application may break. To make it easy to distinguish between positive and negative IDs, all negative entity IDs (such as entity ID -21835 for a coffee shop) are not persisted across versions, while the positive IDs are (such as entity ID 244 for the United States).
Many times, you want to query for entities based on their properties—for example, finding all ATMs in the city of Chicago, or all coffee shops that accept credit cards. In this case, the query is based solely on the entity properties, and you should use the FindServiceSoap.FindByProperty method for this purpose. The FindByProperty method takes a specification object of type FindByPropertySpecification, which takes the queries to find entities using their properties. Table 6-7 shows the fields exposed on theFindByPropertySpecificationobject.
Table 6-7. Fields in the FindByPropertySpecification object
Field
Description
DataSourceName
Name of the data source as a string
Filter
The filter (FindFilterobject) to apply to the results; that is, the specific entity type, properties, and values that the returned results must match
Options
The search options (FindOptionsobject), which may include the range of results and a flag to identify which objects are desired in the returned results
The following code shows how to use these expressions to find entities using theFindByPropertymethod:
//Create a find service soap proxy class FindServiceSoap findService = new FindServiceSoap(); //Assign credentials . . .
//Create find by property specification FindByPropertySpecification findbypropspec = new FindByPropertySpecification();
//Define find by property specification findbypropspec.DataSourceName = "MapPoint.FourthCoffeeSample"; //Assign a filter findbypropspec.Filter = new FindFilter(); //Specify the entity type that you are looking for findbypropspec.Filter.EntityTypeName = "FourthCoffeeShops";
//Now define and assign the expression findbypropspec.Filter.Expression = new FilterExpression(); findbypropspec.Filter.Expression.Text = "PrimaryCity = {0} AND IsWiFiHotSpot"; findbypropspec.Filter.Expression. Parameters = new object[] {"Chicago"};
The resulting expression from this code is"PrimaryCity = 'Chicago' AND IsWiFiHotSpot", which means to return only coffee shops in the city of Chicago that have WiFi Hotspots available. Even though the filter expressions look and behave like SQL expressions, there are limitations that you need to be aware of:
The expression text should never contain the values that are being compared, but the text must provide the placeholders for all non-Boolean value types. Placeholders are represented by “{nn}” where n is an integer between 0 and 9.
The comparison operators LIKE and NOT LIKE support only the “Starts with” condition.
Maximum length of the expression text is limited to 2,000 characters.
No more than one level of nesting (parenthesis) is allowed.
A maximum of 10 non-Boolean comparisons and a maximum of 10 sub-clauses are allowed.
A maximum of 50 total comparisons per expression is allowed.
Next, let’s look at an example expression: you want to find all coffee shops in the city of Chicago that have a seating capacity greater than 20 or that are open 24 hours a day whose names start with the letter C. The expression to pass for theFindByPropertymethod would be:(City={0} AND SeatingCapacity>{1}) OR (StoreType={2} AND Name LIKE {3}) with the arguments Chicago, 20, Open 24 Hours and C.
Now that you know how to use the find service APIs, let’s look at some of the common service methods that are relevant to the finding places, addresses, and entities.
With the find methods, you have seen how to find places, addresses, and points around a place or address, but all you have been finding so far are points (latitude and longitude coordinates). You may have a requirement to find polygons in situations with queries such as: “find all polygons that contain a point (latitude/longitude)” or “find all polygons that have spatial relationship with a rectangle.” In order to accomplish such tasks, use the FindServiceSoap.FindPolygon method.
To learn more about polygons, refer to Appendix B.
Like any find method in Find Service, theFindPolygonmethod takes theFindPolygonSpecificationobject as an argument and returns a validFindResultsobject. TheFindPolygonSpecificationobject provides a way for you to specify arguments such as data source name and spatial filter. Table 6-8 shows the fields exposed by theFindPolygonSpecificationclass.
Table 6-8. Fields of the FindPolygonSpecification class
Field
Description
DataSourceName
Name of the data source as a string
Filter
The filter (FindFilterobject) to apply to the results, including the specific entity type, properties, and values that the returned results must match
Options
The search options (FindOptionsobject), which may include the range of results and a flag to identify which objects are desired in the returned results
SpatialFilter
The spatial filter (SpatialFilterobject) to apply to the results
One interesting field from Table 6-8 is theSpatialFilter field; this field is of typeSpatialFilterclass, and it defines the spatial relationship between polygons, points, and rectangles. TheSpatialFilterclass is an abstract class, and there are two classes that derive this abstract class to define two specific spatial relationships:
LatLongSpatialFilter
Defines a spatial filter that returns only polygons that include the point specified by theLatLongobject. This is used in specifying a spatial filter to find polygons that contain a certain point. This class has only one field that takes the target point as aLatLongobject. The following code shows how to specify aLatLongSpatialFilterto find polygons that contain a given set of latitude and longitude coordinates:
//Create a new instance of LatLongSpatialFilter LatLongSpatialFilter filter = new LatLongSpatialFilter(); //Assign the given latitude and longitude values Filter.LatLong = new LatLong(); Filter.LatLong.Latitude = 47.44; Filter.LatLong.Longitude = -122.55;
LatLongRectangleSpatialFilter
Defines a spatial filter that returns polygons related to theLatLongRectanglespecified via theBoundingRectanglefield. The relation between the polygons and the rectangle is determined by thePolygonRectangleRelationfield. This field is of typeSpatialRelationenumeration and has two values that are shown in Table 6-10. TheLatLongRectangleSpatialFilterclass is used in defining a spatial filter to find polygons that fall within or touch a rectangle. The following code shows how to define this spatial filter to find all polygons that fall within a rectangle:
//Define a new instance of LatLongRectanglSpatialFilter LatLongRectangleSpatialFilter rectangleFilter = new LatLongRectangleSpatialFilter();
//Define a bounding rectangle with north east and south west //corners LatLongRectangle boundingRectangle = new LatLongRectangle(); boundingRectangle.Northeast = new LatLong(); boundingRectangle.Northeast.Latitude = 47.44; boundingRectangle.Northeast.Latitude = -122.56;
boundingRectangle.Southwest = new LatLong(); boundingRectangle.Southwest.Latitude = 41.44; boundingRectangle.Southwest.Latitude = -119.56;
//Now assign bounding rectangle to the filter rectangleFilter.BoundingRectangle = boundingRectangle; //Define the spatial relationship to be //"find polygons inside the rectangle" rectangleFilter.PolygonRectangleRelation = SpatialRelation.WithinArea;
Now that you know how to define spatial filters, let’s look at theFindPolygonmethod in action, using the relations shown in Table 6-9.
Table 6-9. SpatialRelation enumeration
Item
Description
WithinArea
Returns all polygons contained entirely within the specified rectangle
TouchesArea
Returns all polygons that come into contact with the specified rectangle
Use theFindPolygonmethod to findPolygonsthat either contain a specified point or are spatially related to a rectangle. Theymethod takes theFindPolygonSpecificationobject as an argument, as shown in the following code:
//Create an instance of FindServiceSoap and assign //Credentials FindServiceSoap findService = new FindserviceSoap(); //Assign your credentials . . .
//Create an instance of FindPlygonSpecification FindPolygonSpecification findPolySpec = new FindPolygonSpecification();
//Create a new instance of LatLongSpatialFilter LatLongSpatialFilter filter = new LatLongSpatialFilter(); //Assign the given latitude and longitude values Filter.LatLong = new LatLong(); Filter.LatLong.Latitude = 47.44; Filter.LatLong.Longitude = -122.55;
//Assign the spatial filter to the find polygon specification findPolySpec.SpatialFilter=filter;
//Assign your polygon data source findPolySpec.DataSourceName="your polygon data source";
//Define what kind of entities you are looking for FindFilter findfilter = new FindFilter(); findfilter.EntityTypeName = "your entity name"; findPolySpec.Filter = findfilter;
//Call Find Polygon FindResults findResults = findService.FindPolygon(findPolySpec); //Now get the polygon entities foreach(FindResult findResult in findResults.Results) { //Get polygons that matched the query Console.WriteLine(String.Format( "Polygon Entity Matched with ID: {0}", findResult.FoundLocation.Entity.ID) ); }
Now that you have the entity IDs of polygons that match your spatial filter criteria, you can use that information either to render the polygons (covered more in Chapter 8) or to perform any other processing to suit your business needs.
We have looked at find service methods that take place names and addresses and return the corresponding latitude/longitude and other entity information. To find entity (or address) information for any given latitude/longitude, use the FindServiceSoap.GetLocationInfo; it gives you entity information for any given latitude and longitude. The GetLocationInfo method takes theGetInfoOptionsobject as an argument, along with a latitude/longitude and a data source name as a string. TheGetLocationInfoobject gives you control to decide which entity types you want using theGetInfoOptions.EntityTypesToReturnfield; you also have the option to obtain addresses for a given latitude/longitude (if available) using theGetInfoOptions. IncludeAddressesflag. The following code shows how to use theGetLocationInfo method:
//Create a find service soap proxy FindServiceSoap findService = new FindServiceSoap(); //Take an example lat long information that you want to //find using GetLocationInfo LatLong latlong = new LatLong(); latlong.Latitude = 47.682; latlong.Longitude = -122.132;
//Define get info options object GetInfoOptions options = new GetInfoOptions(); //I'm looking only for cities options.IncludeAllEntityTypes = false; options.EntityTypesToReturn = new string[] {"PopulatedPlace"};
//Define a field to hold returned locations Location[] returnedLocations; //Call GetLocationInfo with "MapPoint.NA" data source returnedLocations = findService.GetLocationInfo(latlong, "MapPoint.NA", options); //Get entity information for(int i = 0; i < returnedLocations.Length; i++) { Console.WriteLine(returnedLocations[i].Entity.DisplayName); }
When I’m querying for corresponding entities for the latitude/longitude, I limit my query to a city (the entity namePopulatedPlace) using theEntityTypesToReturnfield; it is important to remember that you must set theIncludeAllEntityTypesto false when you request specific entities.
This method is not a direct inverse to theFindServiceSoap.FindAddressmethod, so if you call theFindServiceSoap.FindAddressmethod to obtain latitude/longitude for an address and pass that latitude/longitude to theFindServiceSoap.GetLocationInfomethod, the resulting address won’t match your original address because of the internal representation of the address data; in MapPoint data sources, addresses are stored in address range blocks along the streets, and the interpolation algorithms are used to calculate the address for a given latitude/longitude and vice versa. So, the addresses returned by theFindServiceSoap.GetLocationInfoare approximations of the original address; in fact, this method returns an array of four possible addresses for any given latitude/longitude in increasing order of the distance from the given latitude/longitude.
Please check back next week for the conclusion to this article.