Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
teobouvard committed Apr 13, 2020
1 parent 6076ddd commit 0b22a64
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 193 deletions.
87 changes: 0 additions & 87 deletions .circleci/config.yml

This file was deleted.

1 change: 0 additions & 1 deletion .dockerignore

This file was deleted.

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
node_modules/
node_modules
keys.js
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
FROM node:10


COPY . /app
WORKDIR /app
RUN yarn install
Expand Down
File renamed without changes.
53 changes: 26 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,57 @@
# Elevation service

[![CircleCI](https://circleci.com/gh/racemap/elevation-service.svg?style=svg)](https://circleci.com/gh/racemap/elevation-service)
Self-hosted elevation service that works with the [terrain data provided by Mapzen and Amazon AWS S3](https://registry.opendata.aws/terrain-tiles/).

Self-hosted elevation service that works with the [terrain data provided by Mapzen and Amazon AWS S3](https://registry.opendata.aws/terrain-tiles/). You can either pre-download the entire data on your server (ca. 200 GB) or access directly on S3 (for minimal latency from `us-east-1` region).
## TL;DR

Try it out with our hosted service: https://elevation.racemap.com/api

Inspired by:
```bash
cp keys_example.js keys.js
hash=$(echo -n MY_API_KEY | sha256sum | awk '{print $1}')
sed -i "s/API_KEY_1/$hash/g" keys.js

- https://github.com/perliedman/elevation-service
- https://github.com/perliedman/node-hgt
yarn install
yarn run test
yarn run start
```

## API usage

!!! This fork REQUIRE an api key for each request with a parameter ?key=APIKEYGOESHERE
Rename existing keys_example.js file, place it in your /etc/keys.js and store properly hashed (sha256 hex format) keys inside the file !!!
### Keys

The service has a very simple API. Just post your latitude-longitude pairs as a JSON array to the service and receive an array of elevations as response. Maximum post payload is by default 700 KB (which fits roughly 10,000 points).
This service requires an API key to answer requests. To set up keys, copy `keys_example.js` as `keys.js`, and add the hash of your keys to the authorized keys array. To generate the hash of your personal key, run

```bash
# > [[lat, lng], ...]
curl -d '[[51.3, 13.4], [51.4, 13.3]]' -XPOST -H 'Content-Type: application/json' http://localhost:3000
# < [ele, ...]
echo -n your-key | sha256sum
```

For one-off queries. You can also issue GET requests with latitude and longitude as query parameters.
### Array of coordinates

```bash
curl 'http://localhost:3000/?lat=51.3&lng=13.4'
# < ele
# > [[lat_1, lon_1], [lat_2, lon_2], ...]
# < [alt_1, alt_2, ...]
curl -d '[[51.3, 13.4], [51.4, 13.3]]' -XPOST -H 'Content-Type: application/json' -L "localhost:4000?key=MY_API_KEY"
```

## Usage with pre-downloaded data

Download data (ca. 200 GB):
### Single coordinate

```bash
aws s3 cp --no-sign-request --recursive s3://elevation-tiles-prod/skadi /path/to/data/folder
curl 'localhost:4000?lat=51.3&lon=13.4'
# < ele
```

Run the docker container:

```bash
docker run --rm -v/path/to/data/folder:/app/data -p3000:3000 racemap/elevation-service
docer build -t elevation-service .
docker run --rm -v/path/to/data/folder:/app/data -4000:4000 elevation-service
```

## Usage with S3-hosted data

Run the docker container:
## Download data

```bash
docker run --rm -eTILE_SET_PATH=s3:// -p3000:3000 racemap/elevation-service
aws s3 cp --no-sign-request --recursive s3://elevation-tiles-prod/skadi /path/to/data/folder
```

## License
## Acknowledgements

MIT
This service was forked from [racemap/elevation-service](https://github.com/racemap/elevation-service).
28 changes: 8 additions & 20 deletions hgt.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { readFile } = require("fs");
const { promisify } = require("util");
const {readFile} = require('fs');
const {promisify} = require('util');

const asyncReadFile = promisify(readFile);

Expand All @@ -10,10 +10,10 @@ function avg(v1, v2, f) {
function bufferStream(stream) {
return new Promise(resolve => {
const bufs = [];
stream.on("data", d => {
stream.on('data', d => {
bufs.push(d);
});
stream.on("end", () => {
stream.on('end', () => {
resolve(Buffer.concat(bufs));
});
});
Expand All @@ -24,13 +24,7 @@ class HGT {
this._buffer = buffer;
this._swLatLng = swLatLng;

this.options = Object.assign(
{},
{
interpolation: HGT.bilinear
},
options
);
this.options = Object.assign({}, {interpolation: HGT.bilinear}, options);

if (buffer.length === 12967201 * 2) {
this._resolution = 1;
Expand All @@ -40,8 +34,7 @@ class HGT {
this._size = 1201;
} else {
throw new Error(
"Unknown tile format (1 arcsecond and 3 arcsecond supported)."
);
'Unknown tile format (1 arcsecond and 3 arcsecond supported).');
}
}

Expand Down Expand Up @@ -99,13 +92,8 @@ class HGT {

if (row < 0 || col < 0 || row > size || col > size) {
throw new Error(
"Latitude/longitude is outside tile bounds (row=" +
row +
", col=" +
col +
"; size=" +
size
);
'Latitude/longitude is outside tile bounds (row=' + row +
', col=' + col + '; size=' + size);
}

return this.options.interpolation.call(this, row, col);
Expand Down
75 changes: 34 additions & 41 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,47 @@
const { json, send } = require("micro");
const limitedMap = require("limited-map");
const query = require("micro-query");
const cors = require("micro-cors")();
const { FileTileSet, S3TileSet } = require("./tileset");
const {json, send} = require('micro');
const limitedMap = require('limited-map');
const query = require('micro-query');
const cors = require('micro-cors')();
const {FileTileSet, S3TileSet} = require('./tileset');
const crypto = require('crypto');
const keys = require('./keys.js');

const cacheSize = process.env.TILE_SET_CACHE || 128;
const tileFolder = process.env.TILE_SET_PATH || __dirname;
const maxPostSize = process.env.MAX_POST_SIZE || "5000kb";
const maxPostSize = process.env.MAX_POST_SIZE || '5000kb';
const maxParallelProcessing = 500;
const allKeys = keys.get();

const tiles = tileFolder.startsWith("s3://")
? new S3TileSet({ cacheSize })
: new FileTileSet(tileFolder, { cacheSize });
const tiles = new FileTileSet(tileFolder, {cacheSize});

async function handlePOST(req, res) {
const payload = await json(req, { limit: maxPostSize });
const payload = await json(req, {limit: maxPostSize});
const reqQuery = query(req);
const apiKey = reqQuery.key;

if (!apiKey) {
return send(res, 400, {
error: keys.message('Missing'),
});
});
}

if (verifyKey(apiKey) < 0) {
return send(res, 400, {
error: keys.message('Invalid'),
});
});
}

if (
!payload ||
!Array.isArray(payload) ||
!payload.every(([lat, lng]) => Number.isFinite(lat) && Number.isFinite(lng))
) {

if (!payload || !Array.isArray(payload) ||
!payload.every(
([lat, lon]) => Number.isFinite(lat) && Number.isFinite(lon))) {
return send(res, 400, {
error:
"Invalid Payload. Expected a JSON array with latitude-longitude pairs: [[lat, lng], ...]"
'Invalid Payload. Expected a JSON array with latitude-longitude pairs: [[lat, lon], ...]'
});
}

const result = await limitedMap(
payload,
ll => tiles.getElevation(ll),
maxParallelProcessing
);
payload, ll => tiles.getElevation(ll), maxParallelProcessing);
return result;
}

Expand All @@ -58,57 +51,57 @@ async function handleGET(req, res) {
if (!apiKey) {
return send(res, 400, {
error: keys.message('Missing'),
});
});
}

if (verifyKey(apiKey) < 0) {
return send(res, 400, {
error: keys.message('Invalid'),
});
});
}

const lat = parseFloat(reqQuery.lat);
const lng = parseFloat(reqQuery.lng);
const lon = parseFloat(reqQuery.lon);
if (lat == null || !Number.isFinite(lat)) {
return send(res, 400, {
error:
"Invalid Latitude. Expected a float number as query parameter: ?lat=12.3&lng=45.6"
'Invalid Latitude. Expected a float number as query parameter: ?lat=12.3&lon=45.6'
});
}
if (lng == null || !Number.isFinite(lng)) {
if (lon == null || !Number.isFinite(lon)) {
return send(res, 400, {
error:
"Invalid Longitude. Expected a float number as query parameter: ?lat=12.3&lng=45.6"
'Invalid Longitude. Expected a float number as query parameter: ?lat=12.3&lon=45.6'
});
}
const result = await tiles.getElevation([lat, lng]);
const result = await tiles.getElevation([lat, lon]);
return result;
}

async function handleGETStatus(req, res) {
return send(res, 200, "Ok");
return send(res, 200, 'Ok');
}

async function handler(req, res) {
switch (req.method) {
case "POST":
case 'POST':
return handlePOST(req, res);
case "GET":
if (req.url == "/status") {
case 'GET':
if (req.url == '/status') {
return handleGETStatus(req, res);
} else {
return handleGET(req, res);
}
case "OPTIONS":
send(res, 200, "");
case 'OPTIONS':
send(res, 200, '');
return;
default:
return send(res, 405, { error: "Only GET or POST allowed" });
return send(res, 405, {error: 'Only GET or POST allowed'});
}
}

// This function verify if the hash of the api key passed by the user
// request matches a key stored inside keys.js file
// This function verifies if the hash of the API key passed by the user request
// matches an authorized key stored inside keys.js file
function verifyKey(key) {
const shasum = crypto.createHash('sha256');
shasum.update(key);
Expand Down
Loading

0 comments on commit 0b22a64

Please sign in to comment.