From 0bcbda12e49523bcb11362583e885943081cdaf3 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Mon, 20 Sep 2021 15:19:39 +0200 Subject: [PATCH 01/15] Implement file based tile cache Add two configuration options: tileCacheFolder, tileCacheLifetime --- src/staticmaps.js | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/staticmaps.js b/src/staticmaps.js index 8e1a6a4..43c9745 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -4,7 +4,9 @@ import find from 'lodash.find'; import uniqBy from 'lodash.uniqby'; import url from 'url'; import chunk from 'lodash.chunk'; - +import { createHash } from 'crypto'; +import fs from 'fs'; +import path from 'path'; import Image from './image'; import IconMarker from './marker'; import Polyline from './polyline'; @@ -29,6 +31,9 @@ class StaticMaps { this.padding = [this.paddingX, this.paddingY]; this.tileUrl = this.options.tileUrl || 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'; this.tileSize = this.options.tileSize || 256; + this.tileCacheFolder = this.options.tileCacheFolder || null; + this.tileCacheLifetime = this.options.tileCacheLifetime || 86400; + this.subdomains = this.options.subdomains || []; this.tileRequestTimeout = this.options.tileRequestTimeout; this.tileRequestHeader = this.options.tileRequestHeader; @@ -567,19 +572,54 @@ class StaticMaps { headers: this.tileRequestHeader || {}, timeout: this.tileRequestTimeout, }; + let cacheFile = null; + + if (this.tileCacheFolder !== null) { + const cacheKey = createHash('sha256').update(data.url).digest('hex'); + cacheFile = path.join(this.tileCacheFolder, cacheKey); + + if (fs.existsSync(cacheFile)) { + const stats = fs.statSync(cacheFile); + + const seconds = (new Date().getTime() - stats.mtime) / 1000; + + // If TTL expire, delete file + if (seconds < this.tileCacheLifetime) { + const cacheData = JSON.parse(fs.readFileSync(cacheFile)); + cacheData.tile.body = new Buffer(cacheData.tile.body, 'base64'); + resolve(cacheData); + return; + } + + fs.rmSync(cacheFile); + } + } // const defaultAgent = `staticmaps@${pjson.version}`; // options.headers['User-Agent'] = options.headers['User-Agent'] || defaultAgent; got.get(options).then((res) => { - resolve({ + const responseContent = { success: true, tile: { url: data.url, box: data.box, body: res.body, }, - }); + }; + + if (this.tileCacheFolder !== null) { + const cacheContent = responseContent; + cacheContent.tile.body = cacheContent.tile.body.toString('base64'); + fs.writeFile(cacheFile, JSON.stringify(cacheContent), (err) => { + if (err) { + console.error(err); + } + // file written successfully + }); + } + + resolve(responseContent); }).catch((error) => resolve({ success: false, error, From 28271dc0eb012d169ab72f6a6f25ef258c7f7baf Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Mon, 20 Sep 2021 15:19:54 +0200 Subject: [PATCH 02/15] extend docs with new options for cache --- README.md | 555 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 315 insertions(+), 240 deletions(-) diff --git a/README.md b/README.md index 8bf3036..8038921 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # StaticMaps [![npm version](https://badge.fury.io/js/staticmaps.svg)](https://badge.fury.io/js/staticmaps) + A Node.js library for creating map images with markers, polylines, polygons and text. This library is a JavaScript implementation of [Static Map](https://github.com/komoot/staticmap). ![Map with polyline](https://stephangeorg.github.io/staticmaps/sample/polyline.png?raw=true=800x280) @@ -9,14 +10,14 @@ Image manipulation is based on **[Sharp](https://sharp.pixelplumbing.com/)**. Pr ## Releases -Version | sharp | node.js (pre-compiled) ------------------ | ---------------- | ------------- -1.6.0+ | 0.29.0 | 12.13.0+ -1.5.2 | 0.28.3 | 10.16.0+ -1.4.4 | 0.27.1 | 10.16.0+ -1.3.4 | 0.25.2 | 10+ -1.2.6 | 0.23.2 | 8, 10, 12, 13 -1.2.3 | 0.22.1 | 6, 8, 10, 11, 12 +| Version | sharp | node.js (pre-compiled) | +| ------- | ------ | ---------------------- | +| 1.6.0+ | 0.29.0 | 12.13.0+ | +| 1.5.2 | 0.28.3 | 10.16.0+ | +| 1.4.4 | 0.27.1 | 10.16.0+ | +| 1.3.4 | 0.25.2 | 10+ | +| 1.2.6 | 0.23.2 | 8, 10, 12, 13 | +| 1.2.3 | 0.22.1 | 6, 8, 10, 11, 12 | [Changelog](https://github.com/StephanGeorg/staticmaps/releases) @@ -28,61 +29,71 @@ Version | sharp | node.js (pre-compiled) ## Getting Started -### Initialization ### +### Initialization + ```javascript -import StaticMaps from 'staticmaps'; +import StaticMaps from "staticmaps"; ``` + ```javascript const options = { width: 600, - height: 400 + height: 400, }; const map = new StaticMaps(options); ``` + #### Map options -Parameter | Default | Description -------------------- | ------------------- | ------------- -width | Required | Width of the output image in px -height | Required | Height of the output image in px -paddingX | 0 | (optional) Minimum distance in px between map features and map border -paddingY | 0 | (optional) Minimum distance in px between map features and map border -tileUrl | | (optional) Tile server URL for the map base layer -tileSize | 256 | (optional) Tile size in pixel -subdomains | [] | (optional) Subdomains of tile server, usage ['a', 'b', 'c'] -tileRequestTimeout | | (optional) Timeout for the tiles request -tileRequestHeader | {} | (optional) Additional headers for the tiles request (default: {}) -tileRequestLimit | 2 | (optional) Limit concurrent connections to the tiles server -zoomRange | { min: 1, max: 17 } | (optional) Defines the range of zoom levels to try -maxZoom | | (optional) DEPRECATED: Use zoomRange.max instead: forces zoom to stay at least this far from the surface, useful for tile servers that error on high levels -reverseY | false | (optional) If true, reverse the y index of the tiles to match the TMS naming format +| Parameter | Default | Description | +| ------------------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| width | Required | Width of the output image in px | +| height | Required | Height of the output image in px | +| paddingX | 0 | (optional) Minimum distance in px between map features and map border | +| paddingY | 0 | (optional) Minimum distance in px between map features and map border | +| tileUrl | | (optional) Tile server URL for the map base layer | +| tileCacheFolder | | (optional) When set to an existing folder, a file cache is used | +| tileCacheLifetime | 86400 | (optional) Time before tile in cache expire and will be reloaded | +| tileSize | 256 | (optional) Tile size in pixel | +| subdomains | [] | (optional) Subdomains of tile server, usage ['a', 'b', 'c'] | +| tileRequestTimeout | | (optional) Timeout for the tiles request | +| tileRequestHeader | {} | (optional) Additional headers for the tiles request (default: {}) | +| tileRequestLimit | 2 | (optional) Limit concurrent connections to the tiles server | +| zoomRange | { min: 1, max: 17 } | (optional) Defines the range of zoom levels to try | +| maxZoom | | (optional) DEPRECATED: Use zoomRange.max instead: forces zoom to stay at least this far from the surface, useful for tile servers that error on high levels | +| reverseY | false | (optional) If true, reverse the y index of the tiles to match the TMS naming format | ### Methods -Method | Description -------------------- | ------------- -[addMarker](#addmarker-options) | Adds a marker to the map -[addLine](#addline-options) | Adds a polyline to the map -[addPolygon](#addpolygon-options) | Adds a polygon to the map -[addMultiPolygon](#addmultipolygon-options) | Adds a multipolygon to the map -[addCircle](#addcircle-options) | Adds a circle to the map -[addText](#addtext-options) | Adds text to the map -[render](#render-center-zoom) | Renders the map and added features -[image.save](#imagesave-filename-outputoptions) | Saves the map image to a file -[image.buffer](#imagebuffer-mime-outputoptions) | Saves the map image to a buffer +| Method | Description | +| ----------------------------------------------- | ---------------------------------- | +| [addMarker](#addmarker-options) | Adds a marker to the map | +| [addLine](#addline-options) | Adds a polyline to the map | +| [addPolygon](#addpolygon-options) | Adds a polygon to the map | +| [addMultiPolygon](#addmultipolygon-options) | Adds a multipolygon to the map | +| [addCircle](#addcircle-options) | Adds a circle to the map | +| [addText](#addtext-options) | Adds text to the map | +| [render](#render-center-zoom) | Renders the map and added features | +| [image.save](#imagesave-filename-outputoptions) | Saves the map image to a file | +| [image.buffer](#imagebuffer-mime-outputoptions) | Saves the map image to a buffer | #### addMarker (options) + Adds a marker to the map. + ##### Marker options -Parameter | Default | Description -------------------- | --------- | ------------- -coord | Required | Coordinates of the marker ([Lng, Lat]) -img | Required | Marker image path or URL -height | Required | Height of the marker image -width | Required | Width of the marker image -offsetX | width/2 | (optional) X offset of the marker image -offsetY | height | (optional) Y offset of the marker image + +| Parameter | Default | Description | +| --------- | -------- | --------------------------------------- | +| coord | Required | Coordinates of the marker ([Lng, Lat]) | +| img | Required | Marker image path or URL | +| height | Required | Height of the marker image | +| width | Required | Width of the marker image | +| offsetX | width/2 | (optional) X offset of the marker image | +| offsetY | height | (optional) Y offset of the marker image | + ##### Usage example + ```javascript const marker = { img: `${__dirname}/marker.png`, // can also be a URL @@ -90,237 +101,288 @@ const marker = { offsetY: 48, width: 48, height: 48, - coord : [13.437524,52.4945528] + coord: [13.437524, 52.4945528], }; map.addMarker(marker); ``` -*** + +--- + #### addLine (options) + Adds a polyline to the map. + ##### Polyline options -Parameter | Default | Description -------------------- | --------- |------------- -coords | Required |Coordinates of the polyline ([[Lng, Lat], ... ,[Lng, Lat]]) -color | #000000BB |Stroke color of the polyline -width | 3 |Stroke width of the polyline + +| Parameter | Default | Description | +| --------- | --------- | ----------------------------------------------------------- | +| coords | Required | Coordinates of the polyline ([[Lng, Lat], ... ,[Lng, Lat]]) | +| color | #000000BB | Stroke color of the polyline | +| width | 3 | Stroke width of the polyline | + ##### Usage example + ```javascript - const polyline = { - coords: [ - [13.399259,52.482659], - [13.387849,52.477144], - [13.40538,52.510632] - ], - color: '#0000FFBB', - width: 3 - }; +const polyline = { + coords: [ + [13.399259, 52.482659], + [13.387849, 52.477144], + [13.40538, 52.510632], + ], + color: "#0000FFBB", + width: 3, +}; - map.addLine(polyline); +map.addLine(polyline); ``` -*** + +--- #### addPolygon (options) + Adds a polygon to the map. Polygon is the same as a polyline but first and last coordinate are equal. + ``` map.addPolygon(options); ``` + ##### Polygon options -Parameter | Default | Description -------------------- | --------- | ------------- -coords | Required | Coordinates of the polygon ([[Lng, Lat], ... ,[Lng, Lat]]) -color | #000000BB | Stroke color of the polygon -width | 3 | Stroke width of the polygon -fill | #000000BB | Fill color of the polygon + +| Parameter | Default | Description | +| --------- | --------- | ---------------------------------------------------------- | +| coords | Required | Coordinates of the polygon ([[Lng, Lat], ... ,[Lng, Lat]]) | +| color | #000000BB | Stroke color of the polygon | +| width | 3 | Stroke width of the polygon | +| fill | #000000BB | Fill color of the polygon | + ##### Usage example + ```javascript - const polygon = { - coords: [ - [13.399259,52.482659], - [13.387849,52.477144], - [13.40538,52.510632], - [13.399259,52.482659] - ], - color: '#0000FFBB', - width: 3 - }; +const polygon = { + coords: [ + [13.399259, 52.482659], + [13.387849, 52.477144], + [13.40538, 52.510632], + [13.399259, 52.482659], + ], + color: "#0000FFBB", + width: 3, +}; - map.addPolygon(polygon); +map.addPolygon(polygon); ``` -*** + +--- #### addMultiPolygon (options) + Adds a multipolygon to the map. + ``` map.addMultiPolygon(options); ``` + ##### Multipolygon options -Parameter | Default | Description -------------------- | --------- | ------------- -coords | Required | Coordinates of the multipolygon ([[Lng, Lat], ... ,[Lng, Lat]]) -color | #000000BB | Stroke color of the multipolygon -width | 3 | Stroke width of the multipolygon -fill | #000000BB | Fill color of the multipolygon + +| Parameter | Default | Description | +| --------- | --------- | --------------------------------------------------------------- | +| coords | Required | Coordinates of the multipolygon ([[Lng, Lat], ... ,[Lng, Lat]]) | +| color | #000000BB | Stroke color of the multipolygon | +| width | 3 | Stroke width of the multipolygon | +| fill | #000000BB | Fill color of the multipolygon | + ##### Usage example + ```javascript - const multipolygon = { - coords: [ - [ - [-89.9619685, 41.7792032], - [-89.959505, 41.7792084], - [-89.9594928, 41.7827904], - [-89.9631906, 41.7827815], - [-89.9632678, 41.7821559], - [-89.9634801, 41.7805341], - [-89.9635341, 41.780109], - [-89.9635792, 41.7796834], - [-89.9636183, 41.7792165], - [-89.9619685, 41.7792032], - ], - [ - [-89.9631647, 41.7809413], - [-89.9632927, 41.7809487], - [-89.9631565, 41.781985], - [-89.9622404, 41.7819137], - [-89.9623616, 41.780997], - [-89.963029, 41.7810114], - [-89.9631647, 41.7809413], - ], +const multipolygon = { + coords: [ + [ + [-89.9619685, 41.7792032], + [-89.959505, 41.7792084], + [-89.9594928, 41.7827904], + [-89.9631906, 41.7827815], + [-89.9632678, 41.7821559], + [-89.9634801, 41.7805341], + [-89.9635341, 41.780109], + [-89.9635792, 41.7796834], + [-89.9636183, 41.7792165], + [-89.9619685, 41.7792032], ], - color: '#0000FFBB', - width: 3 - }; + [ + [-89.9631647, 41.7809413], + [-89.9632927, 41.7809487], + [-89.9631565, 41.781985], + [-89.9622404, 41.7819137], + [-89.9623616, 41.780997], + [-89.963029, 41.7810114], + [-89.9631647, 41.7809413], + ], + ], + color: "#0000FFBB", + width: 3, +}; - map.addMultiPolygon(multipolygon); +map.addMultiPolygon(multipolygon); ``` -*** + +--- #### addCircle (options) + Adds a circle to the map. + ``` map.addPolygon(options); ``` + ##### Polygon options -Parameter | Default | Description -------------------- | --------- | ------------- -coord | Required | Coordinate of center of circle -radius | Required | Circle radius in meter -color | #000000BB | Stroke color of the circle -width | 3 | Stroke width of the circle -fill | #AA0000BB | Fill color of the circle + +| Parameter | Default | Description | +| --------- | --------- | ------------------------------ | +| coord | Required | Coordinate of center of circle | +| radius | Required | Circle radius in meter | +| color | #000000BB | Stroke color of the circle | +| width | 3 | Stroke width of the circle | +| fill | #AA0000BB | Fill color of the circle | + ##### Usage example + ```javascript - const circle = { - coord: [13.01, 51.98], - radius: 500, - fill: '#000000', - width: 0, - }; +const circle = { + coord: [13.01, 51.98], + radius: 500, + fill: "#000000", + width: 0, +}; - map.addCircle(circle); +map.addCircle(circle); ``` -*** + +--- #### addText (options) + Adds text to the map. + ``` map.addText(options) ``` + ##### Text options -Parameter | Default | Description ------------------ | --------- | -------------- -coord | Required | Coordinates of the text ([x, y]) -text | Required | The text to render -color | #000000BB | Stroke color of the text -width | 1px | Stroke width of the text -fill | #000000 | Fill color of the text -size | 12 | Font-size of the text -font | Arial | Font-family of the text -anchor | start | Anchor of the text (`start`, `middle` or `end`) + +| Parameter | Default | Description | +| --------- | --------- | ----------------------------------------------- | +| coord | Required | Coordinates of the text ([x, y]) | +| text | Required | The text to render | +| color | #000000BB | Stroke color of the text | +| width | 1px | Stroke width of the text | +| fill | #000000 | Fill color of the text | +| size | 12 | Font-size of the text | +| font | Arial | Font-family of the text | +| anchor | start | Anchor of the text (`start`, `middle` or `end`) | ##### Usage example + ```javascript - const text = { - coord: [13.437524, 52.4945528], - text: 'My Text', - size: 50, - width: 1, - fill: '#000000', - color: '#ffffff', - font: 'Calibri', - anchor: 'middle' - }; +const text = { + coord: [13.437524, 52.4945528], + text: "My Text", + size: 50, + width: 1, + fill: "#000000", + color: "#ffffff", + font: "Calibri", + anchor: "middle", +}; - map.addText(text); +map.addText(text); ``` -*** +--- #### render (center, zoom) + Renders the map. + ``` map.render(); ``` + ##### Render options -Parameter | Default | Description -------------------- | --------- | ------------- -center | | (optional) Set center of map to a specific coordinate ([Lng, Lat]) -zoom | | (optional) Set a specific zoom level. -*** +| Parameter | Default | Description | +| --------- | ------- | ------------------------------------------------------------------ | +| center | | (optional) Set center of map to a specific coordinate ([Lng, Lat]) | +| zoom | | (optional) Set a specific zoom level. | + +--- #### image.save (fileName, [outputOptions]) + Saves the image to a file in `fileName`. + ``` map.image.save('my-staticmap-image.png', { compressionLevel: 9 }); ``` + ##### Arguments -Parameter | Default | Description -------------------- | ----------- | ------------- -fileName | output.png | Name of the output file. Specify output format (png, jpg, webp) by adding file extension. -outputOptions | | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) -The `outputOptions` replaces the deprectated `quality` option. For Backwards compatibility `quality` still works but will be overwritten with `outputOptions.quality`. +| Parameter | Default | Description | +| ------------- | ---------- | --------------------------------------------------------------------------------------------------- | +| fileName | output.png | Name of the output file. Specify output format (png, jpg, webp) by adding file extension. | +| outputOptions | | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) | +The `outputOptions` replaces the deprectated `quality` option. For Backwards compatibility `quality` still works but will be overwritten with `outputOptions.quality`. ##### Returns + ``` ``` + ~~If callback is undefined it return a Promise.~~ DEPRECATED -*** +--- #### image.buffer (mime, [outputOptions]) + Saves the image to a buffer. + ``` map.image.buffer('image/jpeg', { quality: 75 }); ``` + ##### Arguments -Parameter | Default | Description -------------------- | ----------- | ------------- -mime | image/png | Mime type(`image/png`, `image/jpg` or `image/webp`) of the output buffer -outputOptions | {} | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) + +| Parameter | Default | Description | +| ------------- | --------- | --------------------------------------------------------------------------------------------------- | +| mime | image/png | Mime type(`image/png`, `image/jpg` or `image/webp`) of the output buffer | +| outputOptions | {} | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) | The `outputOptions` replaces the deprectated `quality` option. For Backwards compatibility `quality` still works but will be overwritten with `outputOptions.quality`. ##### Returns + ``` ``` + ~~If callback is undefined it return a Promise.~~ DEPRECATED ## Usage Examples ### Simple map w/ zoom and center + ```javascript const zoom = 13; -const center = [13.437524,52.4945528]; +const center = [13.437524, 52.4945528]; await map.render(center, zoom); -await map.image.save('center.png'); - +await map.image.save("center.png"); ``` + #### Output + ![Map with zoom and center](https://stephangeorg.github.io/staticmaps/sample/center.png) ### Simple map with bounding box @@ -329,18 +391,21 @@ If specifying a bounding box instead of a center, the optimal zoom will be calcu ```javascript const bbox = [ - 11.414795,51.835778, // lng,lat of first point - 11.645164,51.733833 // lng,lat of second point, ... + 11.414795, + 51.835778, // lng,lat of first point + 11.645164, + 51.733833, // lng,lat of second point, ... ]; await map.render(bbox); -await map.image.save('bbox.png'); - +await map.image.save("bbox.png"); ``` + #### Output + ![Map with bbox](https://stephangeorg.github.io/staticmaps/sample/bbox.png) -*** +--- ### Map with single marker @@ -352,146 +417,156 @@ const marker = { width: 48, height: 48, coord: [13.437524, 52.4945528], - }; +}; map.addMarker(marker); await map.render(); -await map.image.save('single-marker.png'); - +await map.image.save("single-marker.png"); ``` + You're free to specify a center as well, otherwise the marker will be centered. #### Output + ![Map with marker](https://stephangeorg.github.io/staticmaps/sample/marker.png) -*** +--- ### Map with multiple marker + ```javascript const marker = { img: `${__dirname}/marker.png`, // can also be a URL offsetX: 24, offsetY: 48, width: 48, - height: 48 + height: 48, }; -marker.coord = [13.437524,52.4945528]; +marker.coord = [13.437524, 52.4945528]; map.addMarker(marker); -marker.coord = [13.430524,52.4995528]; +marker.coord = [13.430524, 52.4995528]; map.addMarker(marker); -marker.coord = [13.410524,52.5195528]; +marker.coord = [13.410524, 52.5195528]; map.addMarker(marker); await map.render(); -await map.image.save('multiple-marker.png'); - +await map.image.save("multiple-marker.png"); ``` + #### Output + ![Map with multiple markers](https://stephangeorg.github.io/staticmaps/sample/multiple-marker.png?raw=true) -*** +--- ### Map with polyline -```javascript +```javascript var line = { coords: [ - [13.399259,52.482659], - [13.387849,52.477144], - [13.40538,52.510632] + [13.399259, 52.482659], + [13.387849, 52.477144], + [13.40538, 52.510632], ], - color: '#0000FFBB', - width: 3 + color: "#0000FFBB", + width: 3, }; map.addLine(line); await map.render(); -await map.image.save('test/out/polyline.png'); - +await map.image.save("test/out/polyline.png"); ``` + #### Output + ![Map with polyline](https://stephangeorg.github.io/staticmaps/sample/polyline.png?raw=true=800x280) -*** +--- ### Map with circle -```javascript - const circle = { +```javascript +const circle = { coord: [13.01, 51.98], radius: 500, - fill: '#000000', + fill: "#000000", width: 0, }; map.addCircle(circle); await map.render(); -await map.image.save('test/out/099-circle.png'); - +await map.image.save("test/out/099-circle.png"); ``` + #### Output + ![Map with circle](https://user-images.githubusercontent.com/7861660/129888175-c2209cca-6ede-43d7-bb8d-181fdd4cfa17.png) -*** +--- ### Blue Marble by NASA with text + ```javascript const options = { - width: 1200, - height: 800, - tileUrl: 'https://map1.vis.earthdata.nasa.gov/wmts-webmerc/BlueMarble_NextGeneration/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg', - zoomRange: { - max: 8, // NASA server does not support level 9 or higher - } - }; - - const map = new StaticMaps(options); - const text = { - coord: [13.437524, 52.4945528], - text: 'My Text', - size: 50, - width: '1px', - fill: '#000000', - color: '#ffffff', - font: 'Calibri' - }; - - map.addText(text); - - await map.render([13.437524, 52.4945528]); - await map.image.save('test/out/bluemarbletext.png'); + width: 1200, + height: 800, + tileUrl: + "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/BlueMarble_NextGeneration/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg", + zoomRange: { + max: 8, // NASA server does not support level 9 or higher + }, +}; + +const map = new StaticMaps(options); +const text = { + coord: [13.437524, 52.4945528], + text: "My Text", + size: 50, + width: "1px", + fill: "#000000", + color: "#ffffff", + font: "Calibri", +}; + +map.addText(text); + +await map.render([13.437524, 52.4945528]); +await map.image.save("test/out/bluemarbletext.png"); ``` #### Output + ![NASA Blue Marble with text](https://i.imgur.com/Jb6hsju.jpg) -*** +--- ### Tile server with subdomains + {s} - subdomain (subdomain), is necessary in order not to fall into the limit for requests to the same domain. Some servers can block your IP if you get tiles from one of subdomains of tile server. + ```javascript const options = { - width: 1024, - height: 1024, - subdomains: ['a', 'b', 'c'], - tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' + width: 1024, + height: 1024, + subdomains: ["a", "b", "c"], + tileUrl: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", }; const map = new StaticMaps(options); await map.render([13.437524, 52.4945528], 13); -await map.image.save('test/out/subdomains.png'); +await map.image.save("test/out/subdomains.png"); ``` # Contributers -+ [Jordi Casadevall Franco](https://github.com/JOGUI22) -+ [Joe Beuckman](https://github.com/jbeuckm) -+ [Ergashev Adizbek](https://github.com/Adizbek) -+ [Olivier Kamers](https://github.com/OlivierKamers) -+ [Wesley Flynn](https://github.com/wesflynn) -+ [Thomas Konings](https://github.com/tkon99) -+ [Gihan S](https://github.com/gihanshp) -+ [Sergey Averyanov](https://github.com/saveryanov) -+ [boxcc](https://github.com/boxcc) -+ [Maksim Skutin](https://github.com/mskutin) +- [Jordi Casadevall Franco](https://github.com/JOGUI22) +- [Joe Beuckman](https://github.com/jbeuckm) +- [Ergashev Adizbek](https://github.com/Adizbek) +- [Olivier Kamers](https://github.com/OlivierKamers) +- [Wesley Flynn](https://github.com/wesflynn) +- [Thomas Konings](https://github.com/tkon99) +- [Gihan S](https://github.com/gihanshp) +- [Sergey Averyanov](https://github.com/saveryanov) +- [boxcc](https://github.com/boxcc) +- [Maksim Skutin](https://github.com/mskutin) From 729170517adeb8db52d8e2136012104fb068e995 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Mon, 20 Sep 2021 15:23:29 +0200 Subject: [PATCH 03/15] revert visual studio code reformatting of README --- README.md | 557 ++++++++++++++++++++++++------------------------------ 1 file changed, 242 insertions(+), 315 deletions(-) diff --git a/README.md b/README.md index 8038921..f47f9ba 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # StaticMaps [![npm version](https://badge.fury.io/js/staticmaps.svg)](https://badge.fury.io/js/staticmaps) - A Node.js library for creating map images with markers, polylines, polygons and text. This library is a JavaScript implementation of [Static Map](https://github.com/komoot/staticmap). ![Map with polyline](https://stephangeorg.github.io/staticmaps/sample/polyline.png?raw=true=800x280) @@ -10,14 +9,14 @@ Image manipulation is based on **[Sharp](https://sharp.pixelplumbing.com/)**. Pr ## Releases -| Version | sharp | node.js (pre-compiled) | -| ------- | ------ | ---------------------- | -| 1.6.0+ | 0.29.0 | 12.13.0+ | -| 1.5.2 | 0.28.3 | 10.16.0+ | -| 1.4.4 | 0.27.1 | 10.16.0+ | -| 1.3.4 | 0.25.2 | 10+ | -| 1.2.6 | 0.23.2 | 8, 10, 12, 13 | -| 1.2.3 | 0.22.1 | 6, 8, 10, 11, 12 | +Version | sharp | node.js (pre-compiled) +----------------- | ---------------- | ------------- +1.6.0+ | 0.29.0 | 12.13.0+ +1.5.2 | 0.28.3 | 10.16.0+ +1.4.4 | 0.27.1 | 10.16.0+ +1.3.4 | 0.25.2 | 10+ +1.2.6 | 0.23.2 | 8, 10, 12, 13 +1.2.3 | 0.22.1 | 6, 8, 10, 11, 12 [Changelog](https://github.com/StephanGeorg/staticmaps/releases) @@ -29,71 +28,63 @@ Image manipulation is based on **[Sharp](https://sharp.pixelplumbing.com/)**. Pr ## Getting Started -### Initialization - +### Initialization ### ```javascript -import StaticMaps from "staticmaps"; +import StaticMaps from 'staticmaps'; ``` - ```javascript const options = { width: 600, - height: 400, + height: 400 }; const map = new StaticMaps(options); ``` - #### Map options -| Parameter | Default | Description | -| ------------------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| width | Required | Width of the output image in px | -| height | Required | Height of the output image in px | -| paddingX | 0 | (optional) Minimum distance in px between map features and map border | -| paddingY | 0 | (optional) Minimum distance in px between map features and map border | -| tileUrl | | (optional) Tile server URL for the map base layer | -| tileCacheFolder | | (optional) When set to an existing folder, a file cache is used | -| tileCacheLifetime | 86400 | (optional) Time before tile in cache expire and will be reloaded | -| tileSize | 256 | (optional) Tile size in pixel | -| subdomains | [] | (optional) Subdomains of tile server, usage ['a', 'b', 'c'] | -| tileRequestTimeout | | (optional) Timeout for the tiles request | -| tileRequestHeader | {} | (optional) Additional headers for the tiles request (default: {}) | -| tileRequestLimit | 2 | (optional) Limit concurrent connections to the tiles server | -| zoomRange | { min: 1, max: 17 } | (optional) Defines the range of zoom levels to try | -| maxZoom | | (optional) DEPRECATED: Use zoomRange.max instead: forces zoom to stay at least this far from the surface, useful for tile servers that error on high levels | -| reverseY | false | (optional) If true, reverse the y index of the tiles to match the TMS naming format | +Parameter | Default | Description +------------------- | ------------------- | ------------- +width | Required | Width of the output image in px +height | Required | Height of the output image in px +paddingX | 0 | (optional) Minimum distance in px between map features and map border +paddingY | 0 | (optional) Minimum distance in px between map features and map border +tileUrl | | (optional) Tile server URL for the map base layer +tileCacheFolder | | (optional) When set to an existing folder, a file cache is used +tileCacheLifetime | 86400 | (optional) Time before tile in cache expire and will be reloaded +tileSize | 256 | (optional) Tile size in pixel +subdomains | [] | (optional) Subdomains of tile server, usage ['a', 'b', 'c'] +tileRequestTimeout | | (optional) Timeout for the tiles request +tileRequestHeader | {} | (optional) Additional headers for the tiles request (default: {}) +tileRequestLimit | 2 | (optional) Limit concurrent connections to the tiles server +zoomRange | { min: 1, max: 17 } | (optional) Defines the range of zoom levels to try +maxZoom | | (optional) DEPRECATED: Use zoomRange.max instead: forces zoom to stay at least this far from the surface, useful for tile servers that error on high levels +reverseY | false | (optional) If true, reverse the y index of the tiles to match the TMS naming format ### Methods -| Method | Description | -| ----------------------------------------------- | ---------------------------------- | -| [addMarker](#addmarker-options) | Adds a marker to the map | -| [addLine](#addline-options) | Adds a polyline to the map | -| [addPolygon](#addpolygon-options) | Adds a polygon to the map | -| [addMultiPolygon](#addmultipolygon-options) | Adds a multipolygon to the map | -| [addCircle](#addcircle-options) | Adds a circle to the map | -| [addText](#addtext-options) | Adds text to the map | -| [render](#render-center-zoom) | Renders the map and added features | -| [image.save](#imagesave-filename-outputoptions) | Saves the map image to a file | -| [image.buffer](#imagebuffer-mime-outputoptions) | Saves the map image to a buffer | +Method | Description +------------------- | ------------- +[addMarker](#addmarker-options) | Adds a marker to the map +[addLine](#addline-options) | Adds a polyline to the map +[addPolygon](#addpolygon-options) | Adds a polygon to the map +[addMultiPolygon](#addmultipolygon-options) | Adds a multipolygon to the map +[addCircle](#addcircle-options) | Adds a circle to the map +[addText](#addtext-options) | Adds text to the map +[render](#render-center-zoom) | Renders the map and added features +[image.save](#imagesave-filename-outputoptions) | Saves the map image to a file +[image.buffer](#imagebuffer-mime-outputoptions) | Saves the map image to a buffer #### addMarker (options) - Adds a marker to the map. - ##### Marker options - -| Parameter | Default | Description | -| --------- | -------- | --------------------------------------- | -| coord | Required | Coordinates of the marker ([Lng, Lat]) | -| img | Required | Marker image path or URL | -| height | Required | Height of the marker image | -| width | Required | Width of the marker image | -| offsetX | width/2 | (optional) X offset of the marker image | -| offsetY | height | (optional) Y offset of the marker image | - +Parameter | Default | Description +------------------- | --------- | ------------- +coord | Required | Coordinates of the marker ([Lng, Lat]) +img | Required | Marker image path or URL +height | Required | Height of the marker image +width | Required | Width of the marker image +offsetX | width/2 | (optional) X offset of the marker image +offsetY | height | (optional) Y offset of the marker image ##### Usage example - ```javascript const marker = { img: `${__dirname}/marker.png`, // can also be a URL @@ -101,288 +92,237 @@ const marker = { offsetY: 48, width: 48, height: 48, - coord: [13.437524, 52.4945528], + coord : [13.437524,52.4945528] }; map.addMarker(marker); ``` - ---- - +*** #### addLine (options) - Adds a polyline to the map. - ##### Polyline options - -| Parameter | Default | Description | -| --------- | --------- | ----------------------------------------------------------- | -| coords | Required | Coordinates of the polyline ([[Lng, Lat], ... ,[Lng, Lat]]) | -| color | #000000BB | Stroke color of the polyline | -| width | 3 | Stroke width of the polyline | - +Parameter | Default | Description +------------------- | --------- |------------- +coords | Required |Coordinates of the polyline ([[Lng, Lat], ... ,[Lng, Lat]]) +color | #000000BB |Stroke color of the polyline +width | 3 |Stroke width of the polyline ##### Usage example - ```javascript -const polyline = { - coords: [ - [13.399259, 52.482659], - [13.387849, 52.477144], - [13.40538, 52.510632], - ], - color: "#0000FFBB", - width: 3, -}; + const polyline = { + coords: [ + [13.399259,52.482659], + [13.387849,52.477144], + [13.40538,52.510632] + ], + color: '#0000FFBB', + width: 3 + }; -map.addLine(polyline); + map.addLine(polyline); ``` - ---- +*** #### addPolygon (options) - Adds a polygon to the map. Polygon is the same as a polyline but first and last coordinate are equal. - ``` map.addPolygon(options); ``` - ##### Polygon options - -| Parameter | Default | Description | -| --------- | --------- | ---------------------------------------------------------- | -| coords | Required | Coordinates of the polygon ([[Lng, Lat], ... ,[Lng, Lat]]) | -| color | #000000BB | Stroke color of the polygon | -| width | 3 | Stroke width of the polygon | -| fill | #000000BB | Fill color of the polygon | - +Parameter | Default | Description +------------------- | --------- | ------------- +coords | Required | Coordinates of the polygon ([[Lng, Lat], ... ,[Lng, Lat]]) +color | #000000BB | Stroke color of the polygon +width | 3 | Stroke width of the polygon +fill | #000000BB | Fill color of the polygon ##### Usage example - ```javascript -const polygon = { - coords: [ - [13.399259, 52.482659], - [13.387849, 52.477144], - [13.40538, 52.510632], - [13.399259, 52.482659], - ], - color: "#0000FFBB", - width: 3, -}; + const polygon = { + coords: [ + [13.399259,52.482659], + [13.387849,52.477144], + [13.40538,52.510632], + [13.399259,52.482659] + ], + color: '#0000FFBB', + width: 3 + }; -map.addPolygon(polygon); + map.addPolygon(polygon); ``` - ---- +*** #### addMultiPolygon (options) - Adds a multipolygon to the map. - ``` map.addMultiPolygon(options); ``` - ##### Multipolygon options - -| Parameter | Default | Description | -| --------- | --------- | --------------------------------------------------------------- | -| coords | Required | Coordinates of the multipolygon ([[Lng, Lat], ... ,[Lng, Lat]]) | -| color | #000000BB | Stroke color of the multipolygon | -| width | 3 | Stroke width of the multipolygon | -| fill | #000000BB | Fill color of the multipolygon | - +Parameter | Default | Description +------------------- | --------- | ------------- +coords | Required | Coordinates of the multipolygon ([[Lng, Lat], ... ,[Lng, Lat]]) +color | #000000BB | Stroke color of the multipolygon +width | 3 | Stroke width of the multipolygon +fill | #000000BB | Fill color of the multipolygon ##### Usage example - ```javascript -const multipolygon = { - coords: [ - [ - [-89.9619685, 41.7792032], - [-89.959505, 41.7792084], - [-89.9594928, 41.7827904], - [-89.9631906, 41.7827815], - [-89.9632678, 41.7821559], - [-89.9634801, 41.7805341], - [-89.9635341, 41.780109], - [-89.9635792, 41.7796834], - [-89.9636183, 41.7792165], - [-89.9619685, 41.7792032], + const multipolygon = { + coords: [ + [ + [-89.9619685, 41.7792032], + [-89.959505, 41.7792084], + [-89.9594928, 41.7827904], + [-89.9631906, 41.7827815], + [-89.9632678, 41.7821559], + [-89.9634801, 41.7805341], + [-89.9635341, 41.780109], + [-89.9635792, 41.7796834], + [-89.9636183, 41.7792165], + [-89.9619685, 41.7792032], + ], + [ + [-89.9631647, 41.7809413], + [-89.9632927, 41.7809487], + [-89.9631565, 41.781985], + [-89.9622404, 41.7819137], + [-89.9623616, 41.780997], + [-89.963029, 41.7810114], + [-89.9631647, 41.7809413], + ], ], - [ - [-89.9631647, 41.7809413], - [-89.9632927, 41.7809487], - [-89.9631565, 41.781985], - [-89.9622404, 41.7819137], - [-89.9623616, 41.780997], - [-89.963029, 41.7810114], - [-89.9631647, 41.7809413], - ], - ], - color: "#0000FFBB", - width: 3, -}; + color: '#0000FFBB', + width: 3 + }; -map.addMultiPolygon(multipolygon); + map.addMultiPolygon(multipolygon); ``` - ---- +*** #### addCircle (options) - Adds a circle to the map. - ``` map.addPolygon(options); ``` - ##### Polygon options - -| Parameter | Default | Description | -| --------- | --------- | ------------------------------ | -| coord | Required | Coordinate of center of circle | -| radius | Required | Circle radius in meter | -| color | #000000BB | Stroke color of the circle | -| width | 3 | Stroke width of the circle | -| fill | #AA0000BB | Fill color of the circle | - +Parameter | Default | Description +------------------- | --------- | ------------- +coord | Required | Coordinate of center of circle +radius | Required | Circle radius in meter +color | #000000BB | Stroke color of the circle +width | 3 | Stroke width of the circle +fill | #AA0000BB | Fill color of the circle ##### Usage example - ```javascript -const circle = { - coord: [13.01, 51.98], - radius: 500, - fill: "#000000", - width: 0, -}; + const circle = { + coord: [13.01, 51.98], + radius: 500, + fill: '#000000', + width: 0, + }; -map.addCircle(circle); + map.addCircle(circle); ``` - ---- +*** #### addText (options) - Adds text to the map. - ``` map.addText(options) ``` - ##### Text options - -| Parameter | Default | Description | -| --------- | --------- | ----------------------------------------------- | -| coord | Required | Coordinates of the text ([x, y]) | -| text | Required | The text to render | -| color | #000000BB | Stroke color of the text | -| width | 1px | Stroke width of the text | -| fill | #000000 | Fill color of the text | -| size | 12 | Font-size of the text | -| font | Arial | Font-family of the text | -| anchor | start | Anchor of the text (`start`, `middle` or `end`) | +Parameter | Default | Description +----------------- | --------- | -------------- +coord | Required | Coordinates of the text ([x, y]) +text | Required | The text to render +color | #000000BB | Stroke color of the text +width | 1px | Stroke width of the text +fill | #000000 | Fill color of the text +size | 12 | Font-size of the text +font | Arial | Font-family of the text +anchor | start | Anchor of the text (`start`, `middle` or `end`) ##### Usage example - ```javascript -const text = { - coord: [13.437524, 52.4945528], - text: "My Text", - size: 50, - width: 1, - fill: "#000000", - color: "#ffffff", - font: "Calibri", - anchor: "middle", -}; + const text = { + coord: [13.437524, 52.4945528], + text: 'My Text', + size: 50, + width: 1, + fill: '#000000', + color: '#ffffff', + font: 'Calibri', + anchor: 'middle' + }; -map.addText(text); + map.addText(text); ``` ---- +*** #### render (center, zoom) - Renders the map. - ``` map.render(); ``` - ##### Render options +Parameter | Default | Description +------------------- | --------- | ------------- +center | | (optional) Set center of map to a specific coordinate ([Lng, Lat]) +zoom | | (optional) Set a specific zoom level. -| Parameter | Default | Description | -| --------- | ------- | ------------------------------------------------------------------ | -| center | | (optional) Set center of map to a specific coordinate ([Lng, Lat]) | -| zoom | | (optional) Set a specific zoom level. | - ---- +*** #### image.save (fileName, [outputOptions]) - Saves the image to a file in `fileName`. - ``` map.image.save('my-staticmap-image.png', { compressionLevel: 9 }); ``` - ##### Arguments - -| Parameter | Default | Description | -| ------------- | ---------- | --------------------------------------------------------------------------------------------------- | -| fileName | output.png | Name of the output file. Specify output format (png, jpg, webp) by adding file extension. | -| outputOptions | | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) | +Parameter | Default | Description +------------------- | ----------- | ------------- +fileName | output.png | Name of the output file. Specify output format (png, jpg, webp) by adding file extension. +outputOptions | | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) The `outputOptions` replaces the deprectated `quality` option. For Backwards compatibility `quality` still works but will be overwritten with `outputOptions.quality`. -##### Returns +##### Returns ``` ``` - ~~If callback is undefined it return a Promise.~~ DEPRECATED ---- +*** #### image.buffer (mime, [outputOptions]) - Saves the image to a buffer. - ``` map.image.buffer('image/jpeg', { quality: 75 }); ``` - ##### Arguments - -| Parameter | Default | Description | -| ------------- | --------- | --------------------------------------------------------------------------------------------------- | -| mime | image/png | Mime type(`image/png`, `image/jpg` or `image/webp`) of the output buffer | -| outputOptions | {} | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) | +Parameter | Default | Description +------------------- | ----------- | ------------- +mime | image/png | Mime type(`image/png`, `image/jpg` or `image/webp`) of the output buffer +outputOptions | {} | (optional) Output options set for [sharp](http://sharp.pixelplumbing.com/en/stable/api-output/#png) The `outputOptions` replaces the deprectated `quality` option. For Backwards compatibility `quality` still works but will be overwritten with `outputOptions.quality`. ##### Returns - ``` ``` - ~~If callback is undefined it return a Promise.~~ DEPRECATED ## Usage Examples ### Simple map w/ zoom and center - ```javascript const zoom = 13; -const center = [13.437524, 52.4945528]; +const center = [13.437524,52.4945528]; await map.render(center, zoom); -await map.image.save("center.png"); -``` +await map.image.save('center.png'); +``` #### Output - ![Map with zoom and center](https://stephangeorg.github.io/staticmaps/sample/center.png) ### Simple map with bounding box @@ -391,21 +331,18 @@ If specifying a bounding box instead of a center, the optimal zoom will be calcu ```javascript const bbox = [ - 11.414795, - 51.835778, // lng,lat of first point - 11.645164, - 51.733833, // lng,lat of second point, ... + 11.414795,51.835778, // lng,lat of first point + 11.645164,51.733833 // lng,lat of second point, ... ]; await map.render(bbox); -await map.image.save("bbox.png"); -``` +await map.image.save('bbox.png'); +``` #### Output - ![Map with bbox](https://stephangeorg.github.io/staticmaps/sample/bbox.png) ---- +*** ### Map with single marker @@ -417,156 +354,146 @@ const marker = { width: 48, height: 48, coord: [13.437524, 52.4945528], -}; + }; map.addMarker(marker); await map.render(); -await map.image.save("single-marker.png"); -``` +await map.image.save('single-marker.png'); +``` You're free to specify a center as well, otherwise the marker will be centered. #### Output - ![Map with marker](https://stephangeorg.github.io/staticmaps/sample/marker.png) ---- +*** ### Map with multiple marker - ```javascript const marker = { img: `${__dirname}/marker.png`, // can also be a URL offsetX: 24, offsetY: 48, width: 48, - height: 48, + height: 48 }; -marker.coord = [13.437524, 52.4945528]; +marker.coord = [13.437524,52.4945528]; map.addMarker(marker); -marker.coord = [13.430524, 52.4995528]; +marker.coord = [13.430524,52.4995528]; map.addMarker(marker); -marker.coord = [13.410524, 52.5195528]; +marker.coord = [13.410524,52.5195528]; map.addMarker(marker); await map.render(); -await map.image.save("multiple-marker.png"); -``` +await map.image.save('multiple-marker.png'); +``` #### Output - ![Map with multiple markers](https://stephangeorg.github.io/staticmaps/sample/multiple-marker.png?raw=true) ---- +*** ### Map with polyline - ```javascript + var line = { coords: [ - [13.399259, 52.482659], - [13.387849, 52.477144], - [13.40538, 52.510632], + [13.399259,52.482659], + [13.387849,52.477144], + [13.40538,52.510632] ], - color: "#0000FFBB", - width: 3, + color: '#0000FFBB', + width: 3 }; map.addLine(line); await map.render(); -await map.image.save("test/out/polyline.png"); -``` +await map.image.save('test/out/polyline.png'); +``` #### Output - ![Map with polyline](https://stephangeorg.github.io/staticmaps/sample/polyline.png?raw=true=800x280) ---- +*** ### Map with circle - ```javascript -const circle = { + + const circle = { coord: [13.01, 51.98], radius: 500, - fill: "#000000", + fill: '#000000', width: 0, }; map.addCircle(circle); await map.render(); -await map.image.save("test/out/099-circle.png"); -``` +await map.image.save('test/out/099-circle.png'); +``` #### Output - ![Map with circle](https://user-images.githubusercontent.com/7861660/129888175-c2209cca-6ede-43d7-bb8d-181fdd4cfa17.png) ---- +*** ### Blue Marble by NASA with text - ```javascript const options = { - width: 1200, - height: 800, - tileUrl: - "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/BlueMarble_NextGeneration/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg", - zoomRange: { - max: 8, // NASA server does not support level 9 or higher - }, -}; - -const map = new StaticMaps(options); -const text = { - coord: [13.437524, 52.4945528], - text: "My Text", - size: 50, - width: "1px", - fill: "#000000", - color: "#ffffff", - font: "Calibri", -}; - -map.addText(text); - -await map.render([13.437524, 52.4945528]); -await map.image.save("test/out/bluemarbletext.png"); + width: 1200, + height: 800, + tileUrl: 'https://map1.vis.earthdata.nasa.gov/wmts-webmerc/BlueMarble_NextGeneration/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg', + zoomRange: { + max: 8, // NASA server does not support level 9 or higher + } + }; + + const map = new StaticMaps(options); + const text = { + coord: [13.437524, 52.4945528], + text: 'My Text', + size: 50, + width: '1px', + fill: '#000000', + color: '#ffffff', + font: 'Calibri' + }; + + map.addText(text); + + await map.render([13.437524, 52.4945528]); + await map.image.save('test/out/bluemarbletext.png'); ``` #### Output - ![NASA Blue Marble with text](https://i.imgur.com/Jb6hsju.jpg) ---- +*** ### Tile server with subdomains - {s} - subdomain (subdomain), is necessary in order not to fall into the limit for requests to the same domain. Some servers can block your IP if you get tiles from one of subdomains of tile server. - ```javascript const options = { - width: 1024, - height: 1024, - subdomains: ["a", "b", "c"], - tileUrl: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + width: 1024, + height: 1024, + subdomains: ['a', 'b', 'c'], + tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' }; const map = new StaticMaps(options); await map.render([13.437524, 52.4945528], 13); -await map.image.save("test/out/subdomains.png"); +await map.image.save('test/out/subdomains.png'); ``` # Contributers -- [Jordi Casadevall Franco](https://github.com/JOGUI22) -- [Joe Beuckman](https://github.com/jbeuckm) -- [Ergashev Adizbek](https://github.com/Adizbek) -- [Olivier Kamers](https://github.com/OlivierKamers) -- [Wesley Flynn](https://github.com/wesflynn) -- [Thomas Konings](https://github.com/tkon99) -- [Gihan S](https://github.com/gihanshp) -- [Sergey Averyanov](https://github.com/saveryanov) -- [boxcc](https://github.com/boxcc) -- [Maksim Skutin](https://github.com/mskutin) ++ [Jordi Casadevall Franco](https://github.com/JOGUI22) ++ [Joe Beuckman](https://github.com/jbeuckm) ++ [Ergashev Adizbek](https://github.com/Adizbek) ++ [Olivier Kamers](https://github.com/OlivierKamers) ++ [Wesley Flynn](https://github.com/wesflynn) ++ [Thomas Konings](https://github.com/tkon99) ++ [Gihan S](https://github.com/gihanshp) ++ [Sergey Averyanov](https://github.com/saveryanov) ++ [boxcc](https://github.com/boxcc) ++ [Maksim Skutin](https://github.com/mskutin) From 8724479495cfe176a2471ea7dcb3e0f3f868079b Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Mon, 20 Sep 2021 16:48:24 +0200 Subject: [PATCH 04/15] fix buffer deprecation --- src/staticmaps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/staticmaps.js b/src/staticmaps.js index 43c9745..fa11e88 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -586,7 +586,7 @@ class StaticMaps { // If TTL expire, delete file if (seconds < this.tileCacheLifetime) { const cacheData = JSON.parse(fs.readFileSync(cacheFile)); - cacheData.tile.body = new Buffer(cacheData.tile.body, 'base64'); + cacheData.tile.body = Buffer.from(cacheData.tile.body, 'base64'); resolve(cacheData); return; } From a45c087f23f7457d8fb5d75ba46085a4977b31fd Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Mon, 20 Sep 2021 18:02:33 +0200 Subject: [PATCH 05/15] Because JS buffer is mutable, we must convert tile back to Buffer --- src/staticmaps.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/staticmaps.js b/src/staticmaps.js index bc8d46c..d221197 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -617,6 +617,10 @@ class StaticMaps { } // file written successfully }); + + if (typeof responseContent.tile.body === 'string') { + responseContent.tile.body = Buffer.from(responseContent.tile.body, 'base64'); + } } resolve(responseContent); From bd6a1870255b482dcf694323b037263756d4f722 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Mon, 20 Sep 2021 22:43:45 +0200 Subject: [PATCH 06/15] cache less information --- src/staticmaps.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/staticmaps.js b/src/staticmaps.js index d221197..3829e52 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -585,9 +585,19 @@ class StaticMaps { // If TTL expire, delete file if (seconds < this.tileCacheLifetime) { - const cacheData = JSON.parse(fs.readFileSync(cacheFile)); - cacheData.tile.body = Buffer.from(cacheData.tile.body, 'base64'); - resolve(cacheData); + let cacheData = JSON.parse(fs.readFileSync(cacheFile)); + cacheData = Buffer.from(cacheData, 'base64'); + + const responseContent = { + success: true, + tile: { + url: data.url, + box: data.box, + body: cacheData, + }, + }; + + resolve(responseContent); return; } @@ -609,9 +619,7 @@ class StaticMaps { }; if (this.tileCacheFolder !== null) { - const cacheContent = responseContent; - cacheContent.tile.body = cacheContent.tile.body.toString('base64'); - fs.writeFile(cacheFile, JSON.stringify(cacheContent), (err) => { + fs.writeFile(cacheFile, JSON.stringify(responseContent.tile.body.toString('base64')), (err) => { if (err) { console.error(err); } From 2237b6eadf8be67a8901f634cc1041428488b8a6 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Tue, 28 Sep 2021 16:20:31 +0200 Subject: [PATCH 07/15] Implement automated cache cleanup, in parallel to image generation --- src/staticmaps.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/staticmaps.js b/src/staticmaps.js index 3829e52..aeca3bc 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -125,9 +125,43 @@ class StaticMaps { this.drawBaselayer(), this.loadMarker(), ]); + + if (this.tileCacheFolder !== null) { + this.clearCache(); + } + return this.drawFeatures(); } + async clearCache() { + if (this.tileCacheFolder !== null) { + // Only clean cache in 20% of all requests + if (Math.random() * 10 >= 1) return; + + const now = new Date().getTime(); + + fs.readdir(this.tileCacheFolder, (err, files) => { + files.forEach((file, index) => { + fs.stat(path.join(this.tileCacheFolder, file), (err, stat) => { + if (err) { + return console.error(err); + } + + const endTime = new Date(stat.ctime).getTime() + this.tileCacheLifetime * 1000; + + if (now > endTime) { + return fs.unlink(path.join(this.tileCacheFolder, file), (err) => { + if (err) { + return console.error(err); + } + }); + } + }); + }); + }); + } + } + /** * calculate common extent of all current map features */ From 43409c06bb3b078a4b143554088f43c3e96e25ca Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Thu, 3 Feb 2022 17:23:22 +0100 Subject: [PATCH 08/15] Implement tileCacheAutoPurge Add Docs for Implement tileCacheAutoPurge Add Docs for calling clearCache manually --- README.md | 8 ++++++++ src/staticmaps.js | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6e4fc88..7058132 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ paddingY | 0 | (optional) Minimum distance in px be tileUrl | | (optional) Tile server URL for the map base layer or `null` for empty base layer tileCacheFolder | | (optional) When set to an existing folder, a file cache is used tileCacheLifetime | 86400 | (optional) Time before tile in cache expire and will be reloaded +tileCacheAutoPurge | true | (optional) Should the Filebased TileCache automatically purged tileSize | 256 | (optional) Tile size in pixel subdomains | [] | (optional) Subdomains of tile server, usage ['a', 'b', 'c'] tileRequestTimeout | | (optional) Timeout for the tiles request @@ -71,6 +72,7 @@ Method | Description [addMultiPolygon](#addmultipolygon-options) | Adds a multipolygon to the map [addCircle](#addcircle-options) | Adds a circle to the map [addText](#addtext-options) | Adds text to the map +[clearCache](#clearcache) | Adds text to the map [render](#render-center-zoom) | Renders the map and added features [image.save](#imagesave-filename-outputoptions) | Saves the map image to a file [image.buffer](#imagebuffer-mime-outputoptions) | Saves the map image to a buffer @@ -273,6 +275,12 @@ center | | (optional) Set center of map to a specific coo zoom | | (optional) Set a specific zoom level. *** +#### clearCache () +clear the file based Tile cache. +Can be used, if tileCacheAutoPurge is set to false to clear the cache +``` +map.clearCache(); +``` #### image.save (fileName, [outputOptions]) Saves the image to a file in `fileName`. diff --git a/src/staticmaps.js b/src/staticmaps.js index cd67854..b4cb97e 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -32,6 +32,8 @@ class StaticMaps { this.tileUrl = 'tileUrl' in this.options ? this.options.tileUrl : 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'; this.tileSize = this.options.tileSize || 256; this.tileCacheFolder = this.options.tileCacheFolder || null; + this.tileCacheAutoPurge = typeof this.options.tileCacheAutoPurge !== 'undefined' ? + this.options.tileCacheAutoPurge : true; this.tileCacheLifetime = this.options.tileCacheLifetime || 86400; this.subdomains = this.options.subdomains || []; @@ -126,9 +128,10 @@ class StaticMaps { this.loadMarker(), ]); - if (this.tileCacheFolder !== null) { + if (this.tileCacheFolder !== null + && this.tileCacheAutoPurge === true) { this.clearCache(); - } + } return this.drawFeatures(); } From ff26d7c71c01c7f8ec4effe0a00846232c0967a1 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Fri, 4 Feb 2022 10:41:28 +0100 Subject: [PATCH 09/15] correct clearCache docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7058132..7a9202a 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Method | Description [addMultiPolygon](#addmultipolygon-options) | Adds a multipolygon to the map [addCircle](#addcircle-options) | Adds a circle to the map [addText](#addtext-options) | Adds text to the map -[clearCache](#clearcache) | Adds text to the map +[clearCache](#clearcache) | Manually clear the base layer tile cache [render](#render-center-zoom) | Renders the map and added features [image.save](#imagesave-filename-outputoptions) | Saves the map image to a file [image.buffer](#imagebuffer-mime-outputoptions) | Saves the map image to a buffer From c135519bc18529bff18f694ad41bbb31902c21b0 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Fri, 4 Feb 2022 11:58:55 +0100 Subject: [PATCH 10/15] add chai-image to compare images during tests --- package-lock.json | 40 ++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 41 insertions(+) diff --git a/package-lock.json b/package-lock.json index 78d78d6..63fa0e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1718,6 +1718,17 @@ "type-detect": "^4.0.5" } }, + "chai-image": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chai-image/-/chai-image-3.0.0.tgz", + "integrity": "sha512-qbGaZvqRJ4bSdqhwrGhsl9+4KIYwqjC/Be56IFyrQLhncqWTjP0sXoEFe4CjLnXX2DabUKSJRZBfSvIUIYhF8Q==", + "dev": true, + "requires": { + "mkdirp": "^1.0.0", + "pixelmatch": "^5.0.2", + "pngjs": "^6.0.0" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3253,6 +3264,12 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -3617,6 +3634,23 @@ "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", "dev": true }, + "pixelmatch": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.2.1.tgz", + "integrity": "sha512-WjcAdYSnKrrdDdqTcVEY7aB7UhhwjYQKYhHiBXdJef0MOaQeYpUdQ+iVyBLa5YBKS8MPVPPMX7rpOByISLpeEQ==", + "dev": true, + "requires": { + "pngjs": "^4.0.1" + }, + "dependencies": { + "pngjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-4.0.1.tgz", + "integrity": "sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg==", + "dev": true + } + } + }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -3626,6 +3660,12 @@ "find-up": "^3.0.0" } }, + "pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "dev": true + }, "prebuild-install": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.0.1.tgz", diff --git a/package.json b/package.json index 9e366b2..4a7a185 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@babel/preset-stage-2": "^7.8.3", "@babel/register": "^7.13.16", "chai": "^4.3.4", + "chai-image": "^3.0.0", "eslint": "^7.27.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-import": "^2.23.3", From 9a4833dc9126a876ce864e8dc86e97234b39c592 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Fri, 4 Feb 2022 12:00:29 +0100 Subject: [PATCH 11/15] ignore cache folder in tests --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3d343c7..3894f7c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules npm-debug.log .DS_Store test/out/* +test/cache/* dist From 562ed14725f6ab9a0f17774cb460869ae0e0f2b8 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Fri, 4 Feb 2022 12:01:55 +0100 Subject: [PATCH 12/15] change cache ttl test from ctime to mtime implement getTileCacheHits to verify during tests --- src/staticmaps.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/staticmaps.js b/src/staticmaps.js index b4cb97e..9540a0e 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -35,6 +35,7 @@ class StaticMaps { this.tileCacheAutoPurge = typeof this.options.tileCacheAutoPurge !== 'undefined' ? this.options.tileCacheAutoPurge : true; this.tileCacheLifetime = this.options.tileCacheLifetime || 86400; + this.tileCacheHits = 0; this.subdomains = this.options.subdomains || []; this.tileRequestTimeout = this.options.tileRequestTimeout; @@ -128,19 +129,23 @@ class StaticMaps { this.loadMarker(), ]); + // when a cache folder is configured and auto purge is enable + // clear cache in 10% of all executions if (this.tileCacheFolder !== null - && this.tileCacheAutoPurge === true) { + && this.tileCacheAutoPurge === true + && Math.random() * 10 <= 1) { this.clearCache(); } return this.drawFeatures(); } + getTileCacheHits() { + return this.tileCacheHits; + } + async clearCache() { if (this.tileCacheFolder !== null) { - // Only clean cache in 20% of all requests - if (Math.random() * 10 >= 1) return; - const now = new Date().getTime(); fs.readdir(this.tileCacheFolder, (err, files) => { @@ -150,9 +155,9 @@ class StaticMaps { return console.error(err); } - const endTime = new Date(stat.ctime).getTime() + this.tileCacheLifetime * 1000; + const fileMTime = new Date(stat.mtime).getTime() + this.tileCacheLifetime * 1000; - if (now > endTime) { + if (now > fileMTime) { return fs.unlink(path.join(this.tileCacheFolder, file), (err) => { if (err) { return console.error(err); @@ -638,6 +643,8 @@ class StaticMaps { }, }; + this.tileCacheHits++; + resolve(responseContent); return; } From 8a2339bbbeaa27370f0146a06a0c4a71575610c7 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Fri, 4 Feb 2022 12:02:05 +0100 Subject: [PATCH 13/15] implement tile cache tests --- test/staticmaps.js | 130 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/test/staticmaps.js b/test/staticmaps.js index 82aceac..8c6d20b 100644 --- a/test/staticmaps.js +++ b/test/staticmaps.js @@ -5,11 +5,18 @@ import GeoJSON from './static/geojson'; import MultiPolygonGeometry from './static/multipolygonGeometry'; import Route from './static/routeLong'; -const { expect } = require('chai'); +import * as chai from "chai"; +import { chaiImage } from "chai-image"; + +chai.use(chaiImage); +const expect = chai.expect; + +const fs = require('fs'); const markerPath = path.join(__dirname, 'marker.png'); describe('StaticMap', () => { + describe('Initializing ...', () => { it('without any arguments', () => { expect(() => { @@ -84,7 +91,7 @@ describe('StaticMap', () => { await map.render([13.437524, 52.4945528], 12); await map.image.save('test/out/04-marker.png'); }).timeout(0); - + /* it('render w/ remote url icon', async () => { const options = { width: 500, @@ -186,6 +193,7 @@ describe('StaticMap', () => { await map.render(); await map.image.save('test/out/05-annotations-nobaselayer.png'); }).timeout(0); + */ }); describe('Rendering w/ polylines ...', () => { @@ -403,5 +411,123 @@ describe('StaticMap', () => { await map.render([13.437524, 52.4945528], 13); await map.image.save('test/out/10-subdomains.png'); }).timeout(0); + }); + + describe('Tile cache', () => { + var cacheFolder = path.resolve(__dirname, 'cache'); + + if (!fs.existsSync(cacheFolder)){ + fs.mkdirSync(cacheFolder); + } + + function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + function clearCache() { + return new Promise((resolve) => { + let files = fs.readdirSync(cacheFolder); + + files.forEach(file => { + fs.unlinkSync(path.join(cacheFolder, file)); + }); + + resolve(); + }); + } + + it('call clearCache manually', async () => { + await clearCache(); + let files = fs.readdirSync(cacheFolder); + expect(files.length).to.be.equal(0, 'cache must start empty'); + const testFilepath = path.join(cacheFolder, 'testfile'); + fs.writeFileSync(testFilepath, 'nothing'); + files = fs.readdirSync(cacheFolder); + expect(files.length).to.be.equal(1, 'we created a single testfile'); + + const options = { + width: 500, + height: 500, + tileCacheFolder: cacheFolder, + tileCacheAutoPurge: false, + tileCacheLifetime: 86400 + }; + + const map = new StaticMaps(options); + map.clearCache(); + + // we must wait, until cache is cleared + await sleep(1000); + + files = fs.readdirSync(cacheFolder); + expect(files.length).to.be.equal(1, 'cache folder must be contain testfile, because within Lifetime'); + + const time = new Date('2000-01-01 18:00:00'); + fs.utimesSync(testFilepath, time, time); + + map.clearCache(); + + // we must wait, until cache is cleared + await sleep(1000); + + files = fs.readdirSync(cacheFolder); + expect(files.length).to.be.equal(0, 'cache folder must be empty after clearCache'); + }).timeout(0); + + it('generate map with cache', async () => { + await clearCache(); + + const options = { + width: 500, + height: 500, + tileCacheFolder: cacheFolder, + tileCacheAutoPurge: false, + tileCacheLifetime: 86400 + }; + + let map = new StaticMaps(options); + + const marker = { + img: markerPath, + offsetX: 24, + offsetY: 48, + width: 48, + height: 48, + }; + + marker.coord = [13.437524, 52.4945528]; + map.addMarker(marker); + + marker.coord = [13.430524, 52.4995528]; + map.addMarker(marker); + + await map.render([13.437524, 52.4945528], 12); + await map.image.save('test/out/11-marker.png'); + + expect(map.getTileCacheHits()).to.be.equal(0); + + let files = fs.readdirSync(cacheFolder); + + map = new StaticMaps(options); + + marker.coord = [13.437524, 52.4945528]; + map.addMarker(marker); + + marker.coord = [13.430524, 52.4995528]; + map.addMarker(marker); + + await map.render([13.437524, 52.4945528], 12); + const bugCompare = fs.readFileSync('test/out/11-marker.png'); + + const bufActual = await map.image.buffer('image/png'); + + // must use all existing cache files + expect(map.getTileCacheHits()).to.be.equal(files.length); + // must match image without cache + expect(bufActual).to.matchImage(bugCompare); + }).timeout(0); + }); }); From 49975926f510cd02964cbe0718b27d64292ef4f5 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Fri, 4 Feb 2022 12:18:02 +0100 Subject: [PATCH 14/15] implement multiple checks for cache consistency --- src/staticmaps.js | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/staticmaps.js b/src/staticmaps.js index 9540a0e..edbc8b9 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -631,22 +631,36 @@ class StaticMaps { // If TTL expire, delete file if (seconds < this.tileCacheLifetime) { - let cacheData = JSON.parse(fs.readFileSync(cacheFile)); - cacheData = Buffer.from(cacheData, 'base64'); - - const responseContent = { - success: true, - tile: { - url: data.url, - box: data.box, - body: cacheData, - }, - }; + let cacheData; + try { + cacheData = JSON.parse(fs.readFileSync(cacheFile)); + } catch(e) { + try { + cacheData = JSON.parse(fs.readFileSync(cacheFile)); + } catch(e) {} + } + + if(cacheData && cacheData.length > 0) { + try { + cacheData = Buffer.from(cacheData, 'base64'); - this.tileCacheHits++; + if(cacheData && cacheData.length > 0) { + const responseContent = { + success: true, + tile: { + url: data.url, + box: data.box, + body: cacheData, + }, + }; - resolve(responseContent); - return; + this.tileCacheHits++; + + resolve(responseContent); + return; + } + } catch (e) {} + } } fs.rmSync(cacheFile); From d77943d89b371c86186154b5ebf73b99b0eb4b66 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Tue, 2 Aug 2022 17:24:22 +0200 Subject: [PATCH 15/15] Merge upstream --- src/staticmaps.js | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/staticmaps.js b/src/staticmaps.js index f4c4907..717672b 100644 --- a/src/staticmaps.js +++ b/src/staticmaps.js @@ -535,6 +535,7 @@ class StaticMaps { * Fetching tile from endpoint */ async getTile(data) { + const options = { url: data.url, responseType: 'buffer', @@ -544,22 +545,6 @@ class StaticMaps { }; try { - let res = await got.get(options); - const { body, headers } = res; - - const contentType = headers['content-type']; - if (!contentType.startsWith('image/')) throw new Error('Tiles server response with wrong data'); - // console.log(headers); -/* - return { - success: true, - tile: { - url: data.url, - box: data.box, - body, - }, - }; -*/ let cacheFile = null; if (this.tileCacheFolder !== null) { @@ -608,14 +593,18 @@ class StaticMaps { } } - res = await got.get(options); + let res = await got.get(options); + const { body, headers } = res; + + const contentType = headers['content-type']; + if (!contentType.startsWith('image/')) throw new Error('Tiles server response with wrong data'); const responseContent = { success: true, tile: { url: data.url, box: data.box, - body: res.body, + body, }, };