It is my pleasure to share with you a Proof of Concept I developed at Code Enhancement Studio that integrates maps and geo-queries to website design and development within the Wix platform, effectively creating a geo-query function.
The Challenge
The client is a wholesale supplier of food products. They want users to be able to look up a specific product supplied by them and then identify disparate grocery companies’ and those companies retail locations. These locations must be within a user-specified radius (in miles) from a location of the user's choice/their choice.
Our initial plan was to use the Wix “address” input, retrieving the (latitude and longitude) geo-coordinate of any retail store location by means of Google’s Place API & Google’s Autocomplete API but unfortunately Wix databases do not support geo-queries.
The Solutions
Completing our research on the Velo Forum & Resources, it appeared that the solution most recommended is to save the records on an online service database that supports geo queries such as MongoDB, Firebase or Elasticsearch.
But this solution required that I build my own query system allowing me to save the locations and query them, which would render the Wix Data API useless (at least for the locations' collection). While this solution is technically possible, it necessitates giving up on the Wix built-in features such as Member Permission(s), dataset binding, and content manager.
Therefore, I elected to build my own geo-query system on Wix using the Geohashes. Without going too far in depth as to how Geohash works, the general concept is:
The world is divided into several geographic regions. Each region is given a unique identifier. These regions are again divided into numerous sub-regions, each with its own unique identifier. By combining identifiers, I can localize a given location on earth. The longer the identifier, the smaller and more accurate the area. By subdividing regions enough times and combining identifiers, they can represent an area of only a few cm anywhere on earth.
Since each geohash is unique and sortable, I can translate any latitude/longitude into a geohash and index any location with geohashes.
Proof of Concept (PoC)
Once the idea was planned out (on paper), I built a simple prototype to test the concept: Wix Starbuck finder. This small Wix app lists all the U.S. Starbucks locations (+6000) on a map and locates the ones nearest to a Wix Office.
I used a dropdown input element with our prototype, but the concept will work equally with an address Input component.
Because Wix does not provide maps with dynamic pins at the moment, I made use of an HTML Iframe housing Google Maps in order to display the item(s).)
BONUS: you can also select the option “Point on the map” and query Starbucks nearby the clicked location.
How It’s Made
As you can see from the code below, the feature is entirely based on the Wix platform, and extends the Wix Data API in order to support geo-queries.
The PoC’s backend provides the following `
getStarbucksAroundPoint
` method:
import wixData from 'wix-data';
import {filterByArea} from "backend/geoQueryService";
const COLLECTION_NAME = "Starbucks";
export function getStarbucksAroundPoint(point, radius) {
const query = wixData.query(COLLECTION_NAME).limit(1000);
const geoQuery = filterByArea(query, point, radius)
return geoQuery.find().then(result => result.items);
}
As you can see, it imports the function `
filterByArea
` from the module `
geoQueryService
`.
This function enhances a
WixDataQuery
object by adding constraints to the field `
geohash
`, based on a point (latitude, longitude) and a radius (in km).
import {getQueryRangesFromRadius} from "./range";
export function filterByArea(baseQuery, point, radius) {
if(!point) throw new Error("no point provided")
const shiftedNeighborsRange = getQueryRangesFromRadius(point.latitude, point.longitude, radius);
const firstRange = shiftedNeighborsRange.shift();
let finalQuery = baseQuery.between("geohash", firstRange[0], firstRange[1])
return shiftedNeighborsRange.reduce((query, range) => {
return query.or(baseQuery.between("geohash", range[0], range[1]))
}, finalQuery);
}
Given a point (latitude, longitude) and radius, the `
getQueryRangeFromRadius
` determines all the regions that are included in the search area. It then adds the constraint on the base query to return the location with all the geohashes within the bounds of matching regions.
The enhanced query is then executed, and the result is returned to the frontend. The main benefit of this system is that I can use wixData API to build the query as I would for any other request (i.e. filter by field x, ordering, using references, limit, skip, etc.).
In Practice
Once I successfully built the PoC, I was able to use it to provide this geo-query function to multiple projects with varying applications. The geo-queries are fast to execute and can quickly filter collections of up to 40,000 locations.
While I haven’t yet had the need to query a greater number of items, I’m confident that, with indexing, I should be able to filter a much larger number of locations.
Code Enhancement Studio thrives on such challenges, and is always looking to push the boundaries of Wix Velo.. Don’t hesitate to contact us with any other boundary-pushing challenge, or if you have any questions regarding geo-queries on Wix Velo.
Comments