Part 3 - Finding Point of Interests
In addition to geocoding addresses, the geocoding service can also be used to find points of interest (POI) of several different kinds. In Part 3, we will explain briefly about category
parameter of geocode()
function and its applications.
Getting started¶
First, Let's look at some Quickstart examples, which show how the category
attributes change based on the search string.
Search for administrative place names¶
The geocode()
method supports single field and multi-field searches for administrative place names. This includes searches for neighborhoods, cities, counties, states, provinces, or countries. If a search for a city name results in multiple matches with the same name, the World Geocoding Service will sort the candidates in order of their relative importance to each other (as indicated by the value of the Rank output field), with priority generally based on population and capital status.
For example, there are many cities in the world named Liverpool
, so a search for "Liverpool" results in several equivalent matches; Liverpool, UK
will always be the top candidate since it has the greatest population.
from arcgis.geocoding import geocode
from arcgis.gis import GIS
gis = GIS("portal url", "username", "password")
address = "Liverpool"
map1 = gis.map("United Kingdom", 6)
map1
liverpool = geocode(address)[0]
map1.draw(liverpool['location'])
However, rank alone is not always enough to distinguish between administrative places. Also, you may not necessarily want to find the highest-ranked feature for a particular search. It may be necessary to remove ambiguity by refining searches with additional information. For example, a search for Liverpool
returns Liverpool, UK
as the top candidate based on rank. If you instead want to find the town of Liverpool, New York, it is necessary to add the state information to the search.
address = {
"Address" : "Liverpool",
"Region" : "NY"
}
map2 = gis.map("Onondaga County, New York, United States")
map2
liverpool = geocode(address)[0]
map2.draw(liverpool['location'])
Example of finding landmarks¶
The geocode()
method can be used to find famous landmarks. The example below geocodes and maps Mt. Everest in Asia, Eiffel Tower in Europe, and the Statue of Liberty in North America:
landmarks = ["Mt. Everest", "Eiffel Tower", "Statue of Liberty"]
map4 = gis.map()
map4
for lm in landmarks:
lm_res = geocode(lm)[0]
map4.draw(lm_res['location'])
Search for postal codes¶
The geocode()
method supports searches for postal codes and postal code extensions. When searching for postal codes, it is important to note that the same code can be valid in more than one country. For the best results, it may be necessary to include additional information with the postal code, such as city or country.
address = {
"Postal" : 110001,
"CountryCode" : "India"
}
map3 = gis.map("New Delhi, India")
map3
pincode = geocode(address)[0]
map3.draw(pincode['location'])
The matched address contains several attributes that provide values for the various output fields supported by the geocoder, as listed below:
pincode['attributes']
Note that if users want to get exact administrative boundaries, they can do so by using geoenrichment as shown here.
Example of finding multiple categories¶
In the example below, we search for Indian and Thai Food in Los Angeles, and plot their locations using different symbols based on the Type
attribute:
categories = "Indian Food, Thai Food"
dtla = geocode("Downtown, Los Angeles, CA")[0]
map5 = gis.map(dtla)
map5
# find and plot up to 100 Indian and Thai restaurants in DTLA
restaurants = geocode(None, dtla['extent'], category=categories, max_locations=100)
thai_symbol = {
"type": "esriSMS",
"style": "esriSMSSquare",
"color": [76,115,0,255],
"size": 8,
"angle": 0,
"xoffset": 0,
"yoffset": 0,
"outline":
{
"color": [152,230,0,255],
"width": 1
}
}
indian_symbol = {
"type": "esriSMS",
"style": "esriSMSCircle",
"color": [115,0,76,255],
"size": 8,
"angle": 0,
"xoffset": 0,
"yoffset": 0,
"outline":
{
"color": [152,230,0,255],
"width": 1
}
}
for restaurant in restaurants:
popup = {
"title" : restaurant['address'],
"content" : "Phone: " + restaurant['attributes']['Phone']
}
if restaurant['attributes']['Type'] == 'Thai Food':
map5.draw(restaurant['location'], popup, thai_symbol) # use a green square symbol for Thai food
else:
map5.draw(restaurant['location'], popup, indian_symbol)
Example of finding restaurants within a polygon drawn on map¶
In the example above, we have learned to search for restaurants in downtown LA. Next we will design a call-back method that could get the geometry of the drawn sketch entered by user and use it as the customized search_extent
for the geocode()
method.
map6 = gis.map()
map6.extent = { 'spatialReference': {'latestWkid': 3857, 'wkid': 102100},
'xmin': -8235706.664189668,
'ymin': 4977993.551288029,
'xmax': -8233351.448255569,
'ymax': 4978949.014141619}
map6
Searching within the area of drawn polygon...
{'xmin': -8237203.157884028, 'ymin': 4975541.833547482, 'xmax': -8234069.23972434, 'ymax': 4977544.483455284, 'spatialReference': {'latestWkid': 3857, 'wkid': 102100}}
Paris Baguette
Melt Bakery
Farmstand
Liquiteria
Sushi Express
Playa Bowls
Ovenly
Starbucks
Toby's Estate Coffee
Sushi On Jones
from arcgis.geometry import Polygon, project, Geometry
# Define the callback function that search within the area.
drawn_polygon = None
def find_restaurants(map1, g):
global drawn_polygon
drawn_polygon = g
print("Searching within the area of drawn polygon...")
search_area = Polygon(g)
search_area_extent = { 'xmin': search_area.extent[0],
'ymin': search_area.extent[1],
'xmax': search_area.extent[2],
'ymax': search_area.extent[3],
'spatialReference': {'latestWkid': 3857, 'wkid': 102100}}
print(search_area_extent)
restaurants = geocode(None, search_extent=search_area_extent, category="Food", max_locations=10)
for restaurant in restaurants:
popup = {
"title" : restaurant['address'],
"content" : "Phone: " + restaurant['attributes']['Phone']
}
map1.draw(restaurant['location'], popup)
print(restaurant['address'])
# Set as the callback function to be invoked when a polygon is drawn on the map
map6.on_draw_end(find_restaurants)
# Either use the geometry defined below
"""
search_area_dict = {'type': 'Polygon', 'coordinates': [[[-73.95949251594702, 40.763214654993785],
[-73.97567231593547, 40.74551709428493],
[-74.00064117977167, 40.76103030464491],
[-73.95949251594702, 40.763214654993785]]]}
search_area = Geometry(search_area_dict)
"""
# Or draw your own polygon
map6.draw("polygon")
Example of finding hospitals within 10 mile buffer around Esri HQ and distance to each¶
Next, let's walk through the example of finding hospitals within a 10-mile buffer around Esri Headquarter (HQ) and computing distances from Esri to each hospital. The steps of implementation in this section would include:
- Creating a Point object for Esri HQ, use the
geometry
module to build abuffer
, and then applying it assearch_extent
parameter. - Getting
geocode()
results as aFeatureSet
, iterating through each feature within, and usingdistance()
to compute the distance from the originalPoint
to the feature. - Presenting results as a
DataFrame
showing different columns along with distance, and plotting the distance as a bar chart. - Plotting the
FeatureSet
on the map with appropriate symbols (using the Python symbol picker for this, or programmatically create these symbols).
Step 1. Create a Point object for Esri HQ, and build a buffer¶
esrihq_fset = geocode("Esri", as_featureset=True)
esrihq_fset
esri_geom = esrihq_fset.features[0]
esri_geom.geometry.JSON
from arcgis.features import Feature, FeatureSet
from arcgis.geometry import buffer
esri_buffer = buffer([esri_geom.geometry],
in_sr = 102100, buffer_sr=102100,
distances=0.1, unit=9001)[0]
esri_buffer_f = Feature(geometry=esri_buffer)
esri_buffer_fset = FeatureSet([esri_buffer_f])
esri_buffer_fset
# need to change the `type` from `MultiPolygon` to `Polygon`
esri_buffer_f_geom_dict = {"type": "Polygon",
"coordinates": esri_buffer_f.geometry.coordinates().tolist()}
Step 2. Geocode and compute distance¶
map7 = gis.map("Redlands, CA")
map7.basemap='gray'
map7
esri_buffer_geom = Geometry(esri_buffer_f_geom_dict)
esri_buffer_geom.extent
fill_symbol = {"type": "esriSFS",
"style": "esriSFSNull",
"outline":{"color": [0,0,0,255]}}
map7.draw(esri_buffer_geom, symbol=fill_symbol)
house_symbol = {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/Shapes/RedStarLargeB.png",
"contentType":"image/png","width":24,"height":24}
map7.draw(esri_geom.geometry, symbol=house_symbol)
search_area_extent = { 'xmin': esri_buffer_geom.extent[0],
'ymin': esri_buffer_geom.extent[1],
'xmax': esri_buffer_geom.extent[2],
'ymax': esri_buffer_geom.extent[3],
'spatialReference': {'latestWkid': 4326, 'wkid': 102100}}
hospitals = geocode('hospital', search_extent=search_area_extent, max_locations=50)
len(hospitals)
hospital_symbol = {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/SafetyHealth/Hospital.png",
"contentType":"image/png","width":24,"height":24}
neighborhood_data_dict = {}
neighborhood_data_dict['hospitals'] = []
for place in hospitals:
popup={"title" : place['attributes']['PlaceName'],
"content" : place['attributes']['Place_addr']}
map7.draw(place['location'], symbol=hospital_symbol, popup=popup)
neighborhood_data_dict['hospitals'].append(place['attributes']['PlaceName'])
for place in hospitals:
print(place["location"])
from arcgis.geometry import Point, distance
neighborhood_data_dict['distance'] = []
for place in hospitals:
dis = distance( spatial_ref=4326,
geometry1=Point(place["location"]),
geometry2=esri_geom.geometry,
geodesic=False,
gis=gis)
neighborhood_data_dict['distance'].append(dis['distance'])
Step 3. Present results in tables and bar charts¶
Note: The distance column is to displayed in units of 100 miles.
import pandas as pd
neighborhood_df = pd.DataFrame.from_dict(neighborhood_data_dict, orient='index')
neighborhood_df = neighborhood_df.transpose()
neighborhood_df
The bar plot below displays the real address on the x-axis for each entry of hospitals on the x-axis.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
addrs = neighborhood_data_dict["hospitals"]
scores = neighborhood_data_dict["distance"]
ax.bar(addrs,scores)
plt.xticks(rotation=90)
plt.show()
Finding POIs using category filtering¶
We have seen selective examples with the category
parameter being used in the geocode()
function above. The steps below show the users how to get the list of all categories the geocoder knows and some usage examples:
Get a list of available categories and sub-categories with the current geocoder¶
from arcgis.geocoding import get_geocoders
geocoder = get_geocoders(gis)[0]
def list_categories(obj, depth = 0):
for category in obj['categories']:
print('\t'*depth + category['name'])
if 'categories' in category:
list_categories(category, depth + 1)
list_categories(geocoder.properties)
An example of finding restaurants around a given location¶
Now let us find restaurants near Time Square (with a maximum of returned results set to 100, since there are way too many restaurants in the specified location).
time_square = geocode("Time Square, NYC")[0]
map8 = gis.map(time_square)
map8
restaurants = geocode(None, time_square['extent'], category="Food", max_locations=100)
for restaurant in restaurants:
popup = {
"title" : restaurant['address'],
"content" : "Phone: " + restaurant['attributes']['Phone']
}
map8.draw(restaurant['location'], popup)
How to refine the results by sub-category¶
Still using the last request to search for restaurants near Time Square, we will further refine the search by sub-categories, e.g. Indian, Chinese, Burgers, Thai.. etc.
The category
parameter is used to specify a place or address type which can be used to filter results. The parameter supports input of single category values
or multiple comma-separated values
. Its usage (shown below) is applicable for all other categories that have sub-categories, besides food. For instance, Education
would include College
, Fine Arts School
, Other Education
, School
and Vocational School
. For more categories and sub-categories, please refer to API Reference.
map9 = gis.map(time_square)
map9
categories = "Indian Food, Chinese Food, Burgers, Thai Food"
chinese_symbol = {
"type": "esriSMS",
"style": "esriSMSSquare",
"color": [115,100,76,55],
"size": 8,
"angle": 0,
"xoffset": 0,
"yoffset": 0,
"outline":
{
"color": [152,230,0,255],
"width": 1
}
}
burgers_symbol = {
"type": "esriSMS",
"style": "esriSMSCircle",
"color": [15,0,176,255],
"size": 8,
"angle": 0,
"xoffset": 0,
"yoffset": 0,
"outline":
{
"color": [152,230,0,255],
"width": 1
}
}
restaurants = geocode(None, time_square['extent'], category=categories, max_locations=100)
for restaurant in restaurants:
popup = {
"title" : restaurant['address'],
"content" : "Phone: " + restaurant['attributes']['Phone']
}
if restaurant['attributes']['Type'] == 'Thai Food':
map9.draw(restaurant['location'], popup, thai_symbol) # green square
elif restaurant['attributes']['Type'] == 'Indian Food':
map9.draw(restaurant['location'], popup, indian_symbol) # dark red circle
elif restaurant['attributes']['Type'] == 'Chinese Food':
map9.draw(restaurant['location'], popup, chinese_symbol) # mint square
else:
map9.draw(restaurant['location'], popup, burgers_symbol) # blue circle
Example of finding gas stations, bars, and other facilities near a given location¶
The example below showcases how to find gas stations, bars, libraries, schools, parks, and grocery stores around a given location, based on the previous sample in which we look for hospitals near Esri Headquarter.
Step 1. Create symbols for facilities¶
(You can get your symbols using this online tool.)
symbols = {"groceries": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/PeoplePlaces/Shopping.png",
"contentType":"image/png","width":12,"height":12},
"coffee": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/PeoplePlaces/Coffee.png",
"contentType":"image/png","width":12,"height":12},
"restaurant": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/PeoplePlaces/Dining.png",
"contentType":"image/png","width":12,"height":12},
"bar": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/PeoplePlaces/Bar.png",
"contentType":"image/png","width":12,"height":12},
"gas": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/Transportation/esriBusinessMarker_72.png",
"contentType":"image/png","width":12,"height":12},
"park": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/OutdoorRecreation/RestArea.png",
"contentType":"image/png","width":10,"height":10},
"school": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/PeoplePlaces/Note.png",
"contentType":"image/png","width":10,"height":10},
"library": {"angle":0,"xoffset":0,"yoffset":0,"type":"esriPMS",
"url":"http://static.arcgis.com/images/Symbols/PeoplePlaces/LiveShow.png",
"contentType":"image/png","width":12,"height":12}}
list(symbols.keys())
Step 2. Define your own geocode function¶
Next, let's define a function to use the ArcGIS Geocoding service in search of facilities around the Esri Headquarter, based on the kind of facilities (e.g. groceries) you are looking for:
def search_and_map(in_map, kind="groceries"):
per_kind = geocode(kind, search_extent=search_area_extent,
max_locations=20, as_featureset=True)
neighborhood_data_dict[kind] = []
for place in per_kind:
popup={"title" : place.attributes['PlaceName'],
"content" : place.attributes['Place_addr']}
in_map.draw(place.geometry, symbol=symbols[kind], popup=popup)
neighborhood_data_dict[kind].append(place.attributes['PlaceName'])
Now, we are ready to loop through the list of facility types and perform geocoding for each kind, then map the results with the customized symbols:
map7.zoom = 13
map7
for kind in list(symbols.keys()) :
search_and_map(map7, kind)
Step 3. Tabularize the results¶
Last but not least, let's present the results in a table:
neighborhood_df = pd.DataFrame.from_dict(neighborhood_data_dict, orient='index')
neighborhood_df = neighborhood_df.transpose()
neighborhood_df
neighborhood_df.count().plot(kind='bar')
plt.title('Facilities within 10 miles of Esri')
Conclusions¶
In Part 3, we have walked through different user scenarios using the category
parameter within the geocode()
function to search and filter geocoded results. In the last scenario, because home buyers often look for access to facilities, such as groceries, restaurants, schools, emergency, and health care, near prospective neighborhoods when shortlisting properties, we used the geocoding
module to search for these facilities, build a table for each property, and map the deliverables.