Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

feat(geo): added geocoder api backend for mapbox and google #541

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/controllers/geocode-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const express = require('express');
const configModel = require('../models/config-model');
const getMapboxResponse = require('../lib/geocoder/mapbox');
const getGoogleResponse = require('../lib/geocoder/google');

function getGeocodeResponse(req, res) {
const provider = configModel.server.geocoder?.provider || 'google';
const geocoders = {
google: getGoogleResponse,
mapbox: getMapboxResponse,
};
const geocoder = geocoders[provider];
if (!geocoder) {
throw new Error(
'Geocoder provider is not configured. Please configure `config.geocoder.provider`'
);
}
geocoder(req.query, (response) => res.status(200).json(response));
}

const router = express.Router();

router.get('/geocoder', getGeocodeResponse);

module.exports = function (app) {
app.use('/api/geo', router);
};
60 changes: 60 additions & 0 deletions app/lib/geocoder/google.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const request = require('request');
const configModel = require('../../models/config-model');

function getGoogleResponse(query, callback) {
query = query || {};
const config = configModel.server.geocoder || {};
const url =
config.url || 'https://maps.googleapis.com/maps/api/geocode/json';

const accessToken =
config['api key'] || configModel.server.google['api key'];

const params = {
...config.params,
key: accessToken,
// proximity: query.$center, // -93.17284130807734,45.070291367515466
// bbox: query.$bbox, // -93.13644718051957,45.05118347242032,-93.17284130807734,45.070291367515466
// limit: query.$limit,
address: query.address,
};

return request(
url,
{
qs: params,
},
(error, response, body) => {
let data;
try {
data = JSON.parse(body);
} catch (e) {
data = {
features: [],
};
}
data = data.results
? data.results.map((f) => ({
geometry: {
type: 'Point',
coordinates: [
f.geometry.location.lon,
f.geometry.location.lat,
], // [125.6, 10.1],
},
id: f.place_id,
type: 'Feature',
properties: {
name: f.formatted_address,
type: f.geometry.location_type,
// score: f.relevance,
// accuracy: f.properties.accuracy,
},
}))
: { error: data.message, status: data.status };
callback(data);
}
);
}

module.exports = getGoogleResponse;
49 changes: 49 additions & 0 deletions app/lib/geocoder/mapbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const request = require('request');
const configModel = require('../../models/config-model');

module.exports = function getMapboxResponse(query, callback) {
const config = configModel.server.geocoder || {};
const url =
config.url ||
'https://api.mapbox.com/geocoding/v5/mapbox.places/{address}.json';
const accessToken = config['api key'];

const params = {
...config.params,
access_token: accessToken,
proximity: query.$center, // -93.17284130807734,45.070291367515466
bbox: query.$bbox, // -93.13644718051957,45.05118347242032,-93.17284130807734,45.070291367515466
limit: query.$limit,
};

return request(
url.replace('{address}', query.address),
{
qs: params,
},
(error, response, body) => {
let data;
try {
data = JSON.parse(body);
} catch (e) {
data = {
features: [],
};
}
data = data.features
? data.features.map((f) => ({
geometry: f.geometry,
id: f.id,
type: f.type,
properties: {
name: f.place_name,
type: f.place_type.join(','),
score: f.relevance,
accuracy: f.properties.accuracy,
},
}))
: { error: data.message };
callback(data);
}
);
};
8 changes: 7 additions & 1 deletion config/default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
},
"api key": ""
},
"geocoder": {
"provider": "google",
"api key": ""
},
"piwik": {
"analytics": {
"tracker url": "",
Expand All @@ -86,7 +90,9 @@
"maps": [
{
"name": "streets",
"tiles": ["https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"],
"tiles": [
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
],
"attribution": "© <a href=\"http://openstreetmap.org\">OpenStreetMap</a> | <a href=\"www.openstreetmap.org/copyright\">Terms</a>"
}
],
Expand Down
16 changes: 16 additions & 0 deletions tutorials/10-configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ Which analytics service you'd like to use, either `"google"` or `"piwik"` or if
- analytics -> domain: If you are running Enketo Express on a subdomain, you may need to add the subdomain there (without protocol), e.g. "odk.enke.to" for Google Analytics to pick up the data. When left empty (`""`) the value will be set to "auto" in the GA script.
- api key: The Google API key that is used for geocoding (i.e. the search box in the geo widgets). Can be obtained [here](https://console.developers.google.com/project). Make sure to enable the _GeoCoding API_ service. If you are using Google Maps layers, the same API key is used. Make sure to enable the _Google Maps JavaScript API v3_ service as well in that case (see next item).

#### geocoder

Enketo allows configuration of different geocoder providers. This configuration is entirely optional, and by default, Google will be used with the same Google api key provided above. If you prefer to use a different api key or provider, you can also configure this using these parameters.

- provider: allows you to change the provider used by the backend geocode service. Current options include `google` and `mapbox`.
- api key: allows you to set the API key for the geocoder provider. This option is required for mapbox and google.

Example:

```json
"geocoder": {
"provider": "mapbox",
"api key": "pk.12345"
},
```

#### piwik

- analytics -> tracker url -> URL on which your piwik service is hosted. The protocol can be omitted, e.g. `"//enketo.piwikpro.com/"`. Required if piwik service is selected under [analytics](#analytics).
Expand Down