How To Map Cities With Vue, GeoJSON, And Google: Part 5
Filter Search Results To City Boundaries
This is the section where we tie together the threads of polygons and search results. Before we dive into that, we sort out our method for getting City Boundaries.
This is part 5 of 5.
- Part 1: Set up Vue.js + Google Maps (move marker when clicked)
- Part 2: Location searching with autocomplete, or update location via mouse click
- Part 3: Get GeoJSON data from nominatim, and draw city boundaries on map
- Part 4: Search via Google Places API, with a fixed radius
- Part 5: Filter search results to occur within boundaries
The repository for this project is at https://github.com/DanielSmith/citymap
In this section:
- need a more consistent method to get Boundaries
- GeoJSON: Polygon vs MultiPolygon
- create polygons that we can check against
- filter search results through containsLocation()
More consistent method to get Boundaries
In earlier parts of this example, I made a call to get GeoJSON data from https://nominatim.openstreetmap.org, and blindly used the first (0) element to get my City Boundary data. As it turns out, there are no guarantees as to which element is of type “city”, or even if there is a city element. Sometimes we need to grab the “administrative” element instead.
If you try a few different searches at https://nominatim.openstreetmap.org, you will see what I mean:
- bodega bay, ca — no city, so we use administrative
- petaluma, ca — we have a city. but it is at the second [1] element
- austin, tx — our city is at the first [0] element
- san mateo, ca — first element is the whole county, we want the city at the second [1] element
In my getCityIndex()
method, I deliberately use a switch statement. This is to make it a) a little more obvious as to which field we prefer, and b) provide an easy spot for you to add in handling for other types (such as “bay” or “highway”)
GeoJSON: Polygon vs MultiPolygon
There are four basic scenarios when it comes to handling GeoJSON data for Boundaries:
- could be a simple polygon :
- could be a polygon with holes:
- could be a multipolygon (note the Statue of Liberty and Ellis Island):
- could be a multipolygon, some with holes:
There is a great explanation of what’s going on with Polygons, Multipolygons, and more at More than you ever wanted to know about GeoJSON
Create polygons that we can check against
I chose to stick with the Google Maps containsLocation()
method, to check if a search result is within a polygon. I could have tried something such as Wicket or Turf.js, but I wanted to keep the number of dependencies down, and show how to work within Google Maps APIs.
To set up a check to see if a point is within a polygon, we need to use getGeometry()
from our data layer (an object of google.maps.Data()
), and build up one or more Polygons with google.maps.Polygon.
(note: I am still new to working with GeoJSON and some of the Google Maps API. My impression is that Google could make this a lot more straightforward, especially when considering this is a product they charge for. I may have missed some simpler approaches. Am happy to hear about those, and to learn)
Earlier on, I showed how there are four cases of what we get back in our GeoJSON request (Polygon vs Multipolygon, possibly with holes). It turns out that we only need to worry about Polygon vs Multipolygon. This lets us know if we need to check more than one Polygon to see if a point is within.
I show a revised getCityData()
below.
The gist of it is:
- we use addGeoJson to add to a data layer
- we want access to the feature that has the geometry, so we set up a variable called
localCity .
The context here is that “local” refers to data we get from the data layer, andgoogleGeometryMultiPoly
is an array we build up, whose elements have been created by callingnew google.maps.Polygon()
- We end up with one (Polygon) or more (Multipolygon) array elements, which will be used when we later do a check for
containsLocation()
- I opted for one code flow here, instead of breaking up attention span by hopping into a bunch of separate methods. This hopefully makes the example a bit more clear.
Filter search results through containsLocation()
Now that we have one or more polygons to compare against (assuming we used “get city” to bring in some Boundaries), we can filter Google Places search results.
Our updated searchResultsMarkers()
will work both ways (filtering, or not). If we need to filter, we do a loop where compare each search result against all polygons. If it is within, it gets added to filteredResults[]
(we set filteredResults
to everything by default, so that we dont need to a compare later on. If it helps, think of the variable as being named “resultsWeAreUsing”.)
Summary
This is fairly stripped down example that covers a lot of topics. Hope you enjoy and get something from it. I will be updating the README.md soon.