Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

lightning-maps

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lightning-maps - npm Package Compare versions

Comparing version 0.0.6 to 0.0.7

CHANGELOG.md

27

package.json

@@ -9,3 +9,4 @@ {

"test": "mocha --require babel-register --colors ./test/*.spec.js",
"test:watch": "mocha --require babel-register --colors -w ./test/*.spec.js"
"test:watch": "mocha --require babel-register --colors -w ./test/*.spec.js",
"bump": "npx standard-version"
},

@@ -30,10 +31,7 @@ "repository": {

"devDependencies": {
"@babel/cli": "^7.0.0-beta.51",
"@babel/core": "^7.0.0-beta.51",
"@babel/preset-env": "^7.0.0-beta.51",
"babel-eslint": "^8.0.3",
"babel-loader": "^8.0.0-beta.4",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-preset-env": "^7.0.0-beta.3",
"babel-register": "^7.0.0-beta.3",
"@babel/cli": "^7.2.3",
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.3.1",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.5",
"chai": "^4.1.2",

@@ -45,2 +43,3 @@ "eslint": "^5.0.1",

"mocha": "^4.0.1",
"standard-version": "^4.4.0",
"webpack": "^4.12.2",

@@ -52,6 +51,12 @@ "webpack-cli": "^3.0.8",

"dependencies": {
"d3-geo": "^1.11.3",
"robust-point-in-polygon": "^1.0.3",
"topojson-client": "^3.0.0"
},
"version": "0.0.6"
"version": "0.0.7",
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
# Lightning Maps (*Alpha release*)
A lightweight, minimal-dependecy slippy map renderer.
A fast, lightweight slippy map renderer with very minimal dependencies.

@@ -10,5 +10,6 @@ Heavily inspired by [Pigeon Maps](https://github.com/mariusandra/pigeon-maps) and [Leaflet](https://leafletjs.com), but with slightly different goals in mind:

* Modern, built using ES6+ syntax
* Lightweight, [minimal dependencies](https://github.com/Geocodio/lightning-maps/blob/master/package.json#L28) with a [minified bundle](https://raw.githubusercontent.com/Geocodio/lightning-maps/master/lib/LightningMaps.min.js) of less than 20kb
* Lightweight, minimal dependencies
* Ability to render thousands of markers, by using `<canvas>` rendering instead of depending on the DOM
* Wrappers for React and VueJS (Coming soon)
* Supports rendering of complex polygons
* Wrapper for React (VueJS coming soon)

@@ -20,3 +21,3 @@ ## Using

```
npm install --save npm lightning-maps
yarn add lightning-maps
```

@@ -27,3 +28,3 @@

```html
<script src="https://unpkg.com/lightning-maps@0.0.1/lib/LightningMaps.min.js"></script>
<script src="https://unpkg.com/lightning-maps@0.0.7/lib/LightningMaps.min.js"></script>
```

@@ -60,7 +61,11 @@

```bash
npm run dev
npm run test:watch
yarn run dev
yarn run test:watch
```
> You can now head to [http://localhost:8080/simple.html](http://localhost:8080/simple.html) or [http://localhost:8080/markers.html](http://localhost:8080/markers.html) to test the app
### Development urls:
* [http://localhost:8080/simple.html](http://localhost:8080/simple.html)
* [http://localhost:8080/markers.html](http://localhost:8080/markers.html)
* [http://localhost:8080/polygons.html](http://localhost:8080/polygons.html)
* [http://localhost:8080/events.html](http://localhost:8080/events.html)

@@ -70,3 +75,3 @@ ### Build library for distribution

```bash
npm run build
yarn run build
```

@@ -46,4 +46,5 @@ export const defaultMapOptions = {

* Used for debouncing events such as scrolling
* Note: Needs to be greater than the animationDurationMs value
*/
debounceIntervalMs: 200,
debounceIntervalMs: 350,

@@ -80,6 +81,40 @@ /**

/**
* What color should the polygon be?
* Whether the polygon should have a stroked line
*/
enableStroke: true,
/**
* What color should the polygon lines be?
* Supports hex, rgb and rgba values
*/
color: 'rgba(0, 0, 200, 0.7)'
strokeStyle: 'rgba(50, 25, 50, 1.0)',
/**
* Specify distances to alternately draw a line and a gap to form
* a dashed or dotted line. Line will be solid if array is empty
*/
lineDash: [],
/**
* Specify the thickness of polygon lines. The width scales with the zoom level, so the actual
* width in pixels is: lineWidth * zoom
*/
lineWidth: 0.25,
/**
* Whether the polygon should be filled with a color
*/
enableFill: true,
/**
* What color should the polygon be filled with?
* Supports hex, rgb and rgba values
*/
fillStyle: 'rgba(0, 0, 0, 0.2)'
};
export const defaultPolygonHoverOptions = {
strokeStyle: 'red',
lineWidth: 0.5
};

@@ -5,3 +5,3 @@ import Map from './Map';

export default {
export {
Map,

@@ -8,0 +8,0 @@ Marker,

@@ -20,6 +20,12 @@ import TileConversion from './TileConversion';

this.attachEvents();
this.applyStyles();
this.lastDrawState = null;
/**
* Events
*/
this.onMarkerClicked = null;
this.onMarkerHover = null;
this.onPolygonHover = null;
this.draw = this.draw.bind(this);

@@ -38,3 +44,3 @@ window.requestAnimationFrame(this.draw);

dragStartPosition: null,
lastEventActionTime: null,
lastZoomEventActionTime: null,
startZoom: this.options.zoom,

@@ -50,3 +56,4 @@ targetZoom: this.options.zoom,

new TileLayer(this)
]
],
mousePosition: { x: 0, y: 0}
};

@@ -60,7 +67,9 @@ }

setZoom(zoom) {
if (this.zoomValueIsValid(zoom)) {
if (this.zoomValueIsValid(zoom) && this.isReadyForZoomEvent()) {
zoom = Math.round(zoom);
this.state.tileLayers.push(new TileLayer(this, zoom));
// this.state.tileLayers[0].tilesZoomLevel = this.options.zoom;
this.state.lastEventActionTime = window.performance.now();
this.state.lastZoomEventActionTime = window.performance.now();
this.state.zoomAnimationStart = window.performance.now();

@@ -108,4 +117,4 @@ this.state.targetZoom = zoom;

isReadyForEvent() {
if (!this.state.lastEventActionTime) {
isReadyForZoomEvent() {
if (!this.state.lastZoomEventActionTime) {
return true;

@@ -115,3 +124,3 @@ }

const now = window.performance.now();
const milliSecondsSinceLastEvent = now - this.state.lastEventActionTime;
const milliSecondsSinceLastEvent = now - this.state.lastZoomEventActionTime;

@@ -129,8 +138,6 @@ return milliSecondsSinceLastEvent > this.options.debounceIntervalMs;

if (this.isReadyForEvent()) {
if (event.deltaY > 5) {
this.setZoom(this.options.zoom - 1);
} else if (event.deltaY < -5) {
this.setZoom(this.options.zoom + 1);
}
if (event.deltaY > 5) {
this.setZoom(this.options.zoom - 1);
} else if (event.deltaY < -5) {
this.setZoom(this.options.zoom + 1);
}

@@ -142,8 +149,7 @@ });

const centerX = this.state.canvasDimensions[0] / 2;
const centerY = this.state.canvasDimensions[1] / 2;
const canvasCenter = this.getCanvasCenter();
this.setTargetMoveOffset(
-(event.clientX - centerX),
-(event.clientY - centerY)
-(event.clientX - canvasCenter[0]),
-(event.clientY - canvasCenter[1])
);

@@ -157,8 +163,10 @@

this.state.mouseVelocities = [];
if (!this.handleMouseEventInteraction(event, 'mousedown')) {
this.state.mouseVelocities = [];
this.state.dragStartPosition = [
event.clientX - this.state.moveOffset[0],
event.clientY - this.state.moveOffset[1]
];
this.state.dragStartPosition = [
event.clientX - this.state.moveOffset[0],
event.clientY - this.state.moveOffset[1]
];
}
});

@@ -169,36 +177,40 @@

const x = -(this.state.dragStartPosition[0] - event.clientX);
const y = -(this.state.dragStartPosition[1] - event.clientY);
if (!this.state.dragStartPosition) {
this.handleMouseEventInteraction(event, 'mouseup');
} else {
const x = -(this.state.dragStartPosition[0] - event.clientX);
const y = -(this.state.dragStartPosition[1] - event.clientY);
if (this.state.moveOffset[0] !== 0 || this.state.moveOffset[1] !== 0) {
const now = window.performance.now();
const timingThreshold = now - this.options.throwTimingThresholdMs;
if (this.state.moveOffset[0] !== 0 || this.state.moveOffset[1] !== 0) {
const now = window.performance.now();
const timingThreshold = now - this.options.throwTimingThresholdMs;
const thresholdsToConsider = this.state.mouseVelocities
.filter(threshold => threshold[0] > timingThreshold)
.map(threshold => threshold[1]);
const thresholdsToConsider = this.state.mouseVelocities
.filter(threshold => threshold[0] > timingThreshold)
.map(threshold => threshold[1]);
const velocitySum = thresholdsToConsider.reduce(
(accumulator, velocity) => accumulator + velocity,
0
);
const velocitySum = thresholdsToConsider.reduce(
(accumulator, velocity) => accumulator + velocity,
0
);
const averageVelocity = velocitySum / thresholdsToConsider.length;
const averageVelocity = velocitySum / thresholdsToConsider.length;
if (averageVelocity >= this.options.throwVelocityThreshold) {
let multiplier = averageVelocity / this.options.throwVelocityThreshold
* this.options.panAccelerationMultiplier;
if (averageVelocity >= this.options.throwVelocityThreshold) {
let multiplier = averageVelocity / this.options.throwVelocityThreshold
* this.options.panAccelerationMultiplier;
multiplier = Math.min(multiplier, this.options.maxPanAcceleration);
multiplier = Math.min(multiplier, this.options.maxPanAcceleration);
this.setTargetMoveOffset(
x * multiplier,
y * multiplier
);
} else {
this.updateCenter();
this.setTargetMoveOffset(
x * multiplier,
y * multiplier
);
} else {
this.updateCenter();
}
}
this.state.dragStartPosition = null;
}
this.state.dragStartPosition = null;
});

@@ -224,2 +236,4 @@

this.state.lastMouseMoveEvent = window.performance.now();
} else {
this.handleMouseEventInteraction(event, 'mousemove');
}

@@ -231,6 +245,2 @@

applyStyles() {
this.canvas.style.cursor = 'grab';
}
easeOutQuad(time) {

@@ -256,4 +266,3 @@ return time * (2 - time);

this.options.zoom,
this.options.tileSize,
this.state.canvasDimensions
this.options.tileSize
);

@@ -383,5 +392,6 @@ }

this.drawMarkers();
this.drawPolygons();
this.drawAttribution();
this.renderPolygons();
this.renderMarkers();
this.renderControls();
this.renderAttribution();
}

@@ -393,4 +403,6 @@

getMapBounds() {
const canvasCenter = this.getCanvasCenter();
const nw = TileConversion.pixelToLatLon(
[this.state.canvasDimensions[0] / 2, (this.state.canvasDimensions[1] / 2)],
[canvasCenter[0], canvasCenter[1]],
this.options.center,

@@ -402,3 +414,3 @@ this.options.zoom,

const se = TileConversion.pixelToLatLon(
[-this.state.canvasDimensions[0] / 2, -(this.state.canvasDimensions[1] / 2)],
[-canvasCenter[0], -canvasCenter[1]],
this.options.center,

@@ -414,15 +426,22 @@ this.options.zoom,

drawMarkers() {
getVisibleMarkers() {
const bounds = this.getMapBounds();
const visibleMarkers = this.state.markers.filter(marker => {
return this.state.markers.filter(marker => {
return marker.coords[0] <= bounds.nw[0] && marker.coords[0] >= bounds.se[0]
&& marker.coords[1] >= bounds.nw[1] && marker.coords[1] <= bounds.se[1];
});
}
const center = [
getCanvasCenter() {
return [
this.state.canvasDimensions[0] / 2,
this.state.canvasDimensions[1] / 2
];
}
renderMarkers() {
const visibleMarkers = this.getVisibleMarkers();
const canvasCenter = this.getCanvasCenter();
visibleMarkers.map(marker => {

@@ -433,9 +452,8 @@ const position = TileConversion.latLonToPixel(

this.options.zoom,
this.options.tileSize,
this.state.canvasDimensions
this.options.tileSize
);
marker.render(this.context, [
center[0] - position[0] + this.state.moveOffset[0],
center[1] - position[1] + this.state.moveOffset[1]
canvasCenter[0] - position[0] + this.state.moveOffset[0],
canvasCenter[1] - position[1] + this.state.moveOffset[1]
]);

@@ -445,6 +463,7 @@ });

drawPolygons() {
renderPolygons() {
const mapState = new MapState(
this.options.center,
this.options.zoom,
this.state.targetZoom,
this.options.tileSize,

@@ -457,9 +476,164 @@ this.state.canvasDimensions,

polygon.render(this.context, mapState);
polygon.handleMouseOver(this.context, mapState, this.state.mousePosition);
});
}
drawAttribution() {
handleMouseEventInteraction(event, name) {
this.state.mousePosition = {
x: event.clientX,
y: event.clientY
};
const controlObjects = this.getControlObjects().filter(item => this.isMouseOverObject(item.bounds));
const markers = controlObjects.length <= 0 && (this.onMarkerClicked || this.onMarkerHover)
? this.getMarkersBounds().filter(item => this.isMouseOverObject(item.bounds))
: [];
if (name === 'mouseup') {
if (controlObjects.length > 0) {
const controlObject = controlObjects[0];
this.setZoom(controlObject.label === '+'
? this.options.zoom + 1
: this.options.zoom - 1);
}
if (this.onMarkerClicked) {
markers.map(item => this.onMarkerClicked(item.marker));
}
} else {
if (this.onMarkerHover) {
markers.map(item => this.onMarkerHover(item.marker));
}
}
let anyItemIsHover = controlObjects.length > 0 || markers.length > 0;
let polygons = [];
if (!anyItemIsHover) {
const mapState = new MapState(
this.options.center,
this.options.zoom,
this.state.targetZoom,
this.options.tileSize,
this.state.canvasDimensions,
this.state.moveOffset
);
polygons = this.state.polygons.map(polygon =>
polygon.handleMouseOver(this.context, mapState, this.state.mousePosition)
).filter(polygon => polygon.length > 0);
}
if (polygons.length > 0) {
anyItemIsHover = true;
if (this.onPolygonHover) {
polygons.map(polygon => polygon.map(item => this.onPolygonHover(item)));
}
}
this.canvas.style.cursor = anyItemIsHover
? 'pointer'
: 'grab';
return anyItemIsHover;
}
getControlObjects() {
const margin = 4;
const size = 30;
return [
{
bounds: {
x: margin,
y: margin,
width: size,
height: size
},
label: '+'
},
{
bounds: {
x: margin,
y: margin + size + margin,
width: size,
height: size
},
label: '-'
}
];
}
getMarkersBounds() {
const visibleMarkers = this.getVisibleMarkers();
const canvasCenter = this.getCanvasCenter();
return visibleMarkers.map(marker => {
const position = TileConversion.latLonToPixel(
marker.coords,
this.options.center,
this.options.zoom,
this.options.tileSize
);
const markerSize = marker.size;
const markerOffset = marker.offset;
return {
bounds: {
x: canvasCenter[0] - position[0] + this.state.moveOffset[0] - (markerSize[0] / 2) + markerOffset[0],
y: canvasCenter[1] - position[1] + this.state.moveOffset[1] - (markerSize[1] / 2) + markerOffset[1],
width: markerSize[0],
height: markerSize[1]
},
marker
};
});
}
renderControls() {
this.getControlObjects().map(item => this.renderControl(item.bounds, item.label));
}
renderControl(bounds, label) {
const radius = 10;
// Background
this.context.fillStyle = this.isMouseOverObject(bounds)
? 'rgba(100, 100, 100, 0.7)'
: 'rgba(0, 0, 0, 0.7)';
this.roundedRectangle(bounds.x, bounds.y, bounds.width, bounds.height, radius);
// Text
this.context.font = 'bold 25px courier';
this.context.textAlign = 'center';
this.context.textBaseline = 'middle';
this.context.fillStyle = 'rgba(255, 255, 255)';
this.context.fillText(
label,
bounds.x + (bounds.width / 2),
bounds.y + (bounds.height / 2)
);
}
isMouseOverObject(bounds) {
return this.state.mousePosition.x >= bounds.x
&& this.state.mousePosition.x <= bounds.x + bounds.width
&& this.state.mousePosition.y >= bounds.y
&& this.state.mousePosition.y <= bounds.y + bounds.height;
}
renderAttribution() {
const margin = 4;
this.context.font = 'bold 12px sans-serif';
this.context.textAlign = 'left';
this.context.textBaseline = 'alphabetic';
const textBounds = this.context.measureText(this.options.attribution);

@@ -475,8 +649,5 @@

this.context.fillText(this.options.attribution, x, y);
}
roundedRectangle(x, y, width, height) {
const radius = 5;
roundedRectangle(x, y, width, height, radius = 5) {
this.context.beginPath();

@@ -505,2 +676,6 @@ this.context.moveTo(x + radius, y);

setMarkers(markers) {
this.state.markers = markers;
}
addPolygon(polygon) {

@@ -510,2 +685,6 @@ this.state.polygons.push(polygon);

setPolygons(polygons) {
this.state.polygons = polygons;
}
}
export default class MapState {
constructor(center, zoom, tileSize, canvasDimensions, moveOffset) {
constructor(center, zoom, targetZoom, tileSize, canvasDimensions, moveOffset) {
this._center = center;
this._zoom = zoom;
this._targetZoom = targetZoom;
this._tileSize = tileSize;

@@ -18,2 +19,6 @@ this._canvasDimensions = canvasDimensions;

get targetZoom() {
return this._targetZoom;
}
get tileSize() {

@@ -20,0 +25,0 @@ return this._tileSize;

@@ -17,2 +17,37 @@ import { defaultMarkerOptions } from './defaultOptions';

get size() {
switch (this.options.type) {
case 'marker':
return [
17.698069,
24.786272
];
case 'circle':
return [10, 10];
case 'donut':
return [14, 14];
case 'image':
return this.options.image
? [this.options.image.width, this.options.image.height]
: null;
default:
return null;
}
}
get offset() {
if (this.options.type === 'marker') {
return [
0,
-(this.size[1] / 2)
];
}
return [0, 0];
}
render(context, position) {

@@ -33,2 +68,6 @@ let renderFunction = null;

break;
case 'image':
renderFunction = this.renderImage;
break;
}

@@ -50,3 +89,3 @@

context.beginPath();
context.arc(position[0], position[1], 5, 0, 2 * Math.PI);
context.arc(position[0], position[1], this.size[0] / 2, 0, 2 * Math.PI);
context.fill();

@@ -60,3 +99,3 @@ context.restore();

context.lineWidth = 5;
context.arc(position[0], position[1], 7, 0, 2 * Math.PI);
context.arc(position[0], position[1], this.size[0] / 2, 0, 2 * Math.PI);
context.stroke();

@@ -67,7 +106,6 @@ context.restore();

renderMarker(context, position) {
const markerWidth = 17.698069;
const markerHeight = 24.786272;
const size = this.size;
const x = position[0] - markerWidth / 2;
const y = position[1] - markerHeight;
const x = position[0] - size[0] / 2;
const y = position[1] - size[1];

@@ -91,2 +129,12 @@ context.save();

}
renderImage(context, position) {
if (this.options.image) {
const size = this.size;
const x = position[0] - size[0] / 2;
const y = position[1] - size[1] / 2;
context.drawImage(this.options.image, x, y, size[0], size[1]);
}
}
}

@@ -1,26 +0,14 @@

import { defaultPolygonOptions } from './defaultOptions';
import { defaultPolygonOptions, defaultPolygonHoverOptions } from './defaultOptions';
import TileConversion from './TileConversion';
import { geoPath, geoTransform } from 'd3-geo';
import { mesh } from 'topojson-client';
import { feature } from 'topojson-client';
import classifyPoint from 'robust-point-in-polygon';
const POLYGON_CACHE = {};
export default class Polygon {
constructor(sourceUrl, options = {}) {
this._sourceUrl = sourceUrl;
constructor(json, objectName, options = {}, hoverOptions = null) {
this._options = Object.assign({}, defaultPolygonOptions, options);
this._geometry = null;
this._hoverOptions = Object.assign({}, defaultPolygonOptions, defaultPolygonHoverOptions, hoverOptions);
fetch(this._sourceUrl)
.then(response => response.json())
.then(json => {
this._geometry = json;
})
.catch(err => console.log(`Could not load ${this._sourceUrl}: ${err.message || err}`));
this.geometry = feature(json, json.objects[objectName]);
}
get sourceUrl() {
return this._sourceUrl;
}
get options() {

@@ -30,57 +18,306 @@ return this._options;

get hoverOptions() {
return this._hoverOptions;
}
get geometry() {
return window._geometry;
}
set geometry(geometry) {
window._geometry = geometry;
}
get projectedGeometry() {
return window._projectedGeometry;
}
set projectedGeometry(projectedGeometry) {
window._projectedGeometry = projectedGeometry;
}
get projectedGeometryState() {
return this._projectedGeometryState;
}
set projectedGeometryState(projectedGeometryState) {
this._projectedGeometryState = projectedGeometryState;
}
handleMouseOver(context, mapState, mousePosition) {
const currentlyZooming = Math.round(mapState.zoom) !== mapState.zoom;
if (!this.geometry || !this.projectedGeometry || currentlyZooming) {
return [];
}
const canvasCenter = [
mapState.canvasDimensions[0] / 2,
mapState.canvasDimensions[1] / 2
];
const originZoom = this.determineOriginZoom(mapState);
const centerOffset = this.calculateCenterOffset(mapState, originZoom);
const point = [
mousePosition.x - centerOffset[0] - canvasCenter[0],
mousePosition.y - centerOffset[1] - canvasCenter[1]
];
return this.projectedGeometry.filter(item => {
const isHover = item.geometry.filter(list => classifyPoint(list, point) === -1).length > 0;
if (isHover) {
context.beginPath();
item.geometry.map((list) => {
list.map((position, index) => {
position = [
position[0] + centerOffset[0] + canvasCenter[0],
position[1] + centerOffset[1] + canvasCenter[1]
];
if (index === 0) {
context.moveTo(position[0], position[1]);
} else {
context.lineTo(position[0], position[1]);
}
});
});
this.applyContextStyles(context, this.hoverOptions, mapState.zoom);
if (this.options.enableStroke) context.fill();
if (this.options.enableFill) context.stroke();
}
return isHover;
});
}
calculateCenterOffset(mapState, originZoom) {
return [
-(TileConversion.lon2tile(mapState.center[1], originZoom, false) * mapState.tileSize),
-(TileConversion.lat2tile(mapState.center[0], originZoom, false) * mapState.tileSize)
];
}
render(context, mapState) {
if (!this._geometry) {
if (!this.geometry) {
return;
}
context.fillStyle = this.options.color;
context.strokeStyle = this.options.color;
const originZoom = this.determineOriginZoom(mapState);
this.mapState = mapState;
const zoomDiff = originZoom
? mapState.zoom - originZoom
: 0;
const center = [
this.mapState.canvasDimensions[0] / 2,
this.mapState.canvasDimensions[1] / 2
const scale = zoomDiff !== 0
? Math.pow(2, zoomDiff)
: 1;
const mapCenterChanged = this.renderedMapCenter !== mapState.center,
mapZoomChanged = (zoomDiff === 0 && this.renderedZoomLevel !== mapState.zoom);
const shouldReRender = mapCenterChanged || mapZoomChanged;
let centerOffset = null;
if (shouldReRender) {
this.renderedZoomLevel = mapState.zoom;
this.renderedMapCenter = mapState.center;
this.projectedGeometry = this.geometry.features.map(feature => {
return {
...feature,
geometry: this.projectGeometry(feature.geometry, mapState)
};
});
centerOffset = this.calculateCenterOffset(mapState, originZoom);
this.calculatePolygonExtends(centerOffset);
}
const canvasCenter = [
mapState.canvasDimensions[0] / 2,
mapState.canvasDimensions[1] / 2
];
const transform = geoTransform({point: this.projectPoint, mapState, center });
const imagePosition = [
canvasCenter[0] + mapState.moveOffset[0] + (this.polygonExtends.minX * scale),
canvasCenter[1] + mapState.moveOffset[1] + (this.polygonExtends.minY * scale)
];
const path = geoPath(transform).context(context);
const imageRect = {
left: Math.floor(imagePosition[0] * -1),
right: Math.ceil(Math.abs(imagePosition[0]) + mapState.canvasDimensions[0]),
top: Math.floor(imagePosition[1] * -1),
bottom: Math.ceil(Math.abs(imagePosition[1]) + mapState.canvasDimensions[1])
};
context.beginPath();
path(mesh(this._geometry));
context.stroke();
if (shouldReRender) {
this.renderOffscreenCanvas(mapState, centerOffset, imageRect);
}
const imageDrawPosition = [
mapState.moveOffset[0] - (canvasCenter[0] * (scale - 1)),
mapState.moveOffset[1] - (canvasCenter[1] * (scale - 1))
];
const scaledWidth = this.polygonDimensions[0] * scale,
scaledHeight = this.polygonDimensions[1] * scale;
context.drawImage(
this.offscreenCanvas,
imageDrawPosition[0], imageDrawPosition[1],
scaledWidth, scaledHeight
);
}
projectPoint(x, y) {
const cachedPosition = (x, y, mapState) => {
const cacheKey = JSON.stringify([
[y, x], this.mapState.center, this.mapState.zoom,
this.mapState.tileSize, this.mapState.canvasDimensions
]);
determineOriginZoom(mapState) {
let originZoom = mapState.zoom;
if (cacheKey in POLYGON_CACHE) {
return POLYGON_CACHE[cacheKey];
if (mapState.targetZoom > mapState.zoom) { // Zoming in
originZoom = Math.floor(mapState.zoom);
} else if (mapState.targetZoom < mapState.zoom) { // Zooming out
originZoom = Math.ceil(mapState.zoom);
}
return originZoom;
}
createOffscreenCanvas(clipRect) {
this.polygonDimensions = [
clipRect.right - clipRect.left,
clipRect.bottom - clipRect.top
];
this.offscreenCanvas = document.createElement('canvas');
this.offscreenCanvas.width = this.polygonDimensions[0];
this.offscreenCanvas.height = this.polygonDimensions[1];
return this.offscreenCanvas.getContext('2d');
}
calculatePolygonExtends(centerOffset) {
let minX, maxX, minY, maxY = null;
this.mapGeometry(position => {
position = [
position[0] + centerOffset[0],
position[1] + centerOffset[1]
];
if (!maxX || position[0] > maxX) {
maxX = position[0];
}
const position = TileConversion.latLonToPixel(
[y, x],
this.mapState.center,
this.mapState.zoom,
this.mapState.tileSize,
this.mapState.canvasDimensions
);
if (!minX || position[0] < minX) {
minX = position[0];
}
POLYGON_CACHE[cacheKey] = position;
if (!maxY || position[1] > maxY) {
maxY = position[1];
}
return position;
if (!minY || position[1] < minY) {
minY = position[1];
}
});
this.polygonDimensions = [
Math.ceil(maxX - minX),
Math.ceil(maxY - minY)
];
this.polygonExtends = {
minX, maxX,
minY, maxY
};
}
const position = cachedPosition(x, y, this.mapState);
renderOffscreenCanvas(mapState, centerOffset, clipRect) {
const offscreenContext = this.createOffscreenCanvas(clipRect);
const projectedX = this.center[0] - position[0] + this.mapState.moveOffset[0];
const projectedY = this.center[1] - position[1] + this.mapState.moveOffset[1];
offscreenContext.beginPath();
offscreenContext.font = 'bold 8px helvetica';
this.stream.point(projectedX, projectedY);
this.projectedGeometry.map((item) =>
item.geometry.map((list) => {
const pointsInClipRect = list.filter(position => {
position = [
position[0] - this.polygonExtends.minX + centerOffset[0],
position[1] - this.polygonExtends.minY + centerOffset[1]
];
return position[0] >= clipRect.left && position[0] <= clipRect.right
&& position[1] >= clipRect.top && position[1] <= clipRect.bottom;
});
if (pointsInClipRect.length > 0) {
list.map((position, index) => {
position = [
position[0] - this.polygonExtends.minX + centerOffset[0] - clipRect.left,
position[1] - this.polygonExtends.minY + centerOffset[1] - clipRect.top
];
if (index === 0) {
// offscreenContext.fillText(item.properties.NAME, position[0], position[1]);
offscreenContext.moveTo(position[0], position[1]);
} else {
offscreenContext.lineTo(position[0], position[1]);
}
});
}
})
);
this.applyContextStyles(offscreenContext, this.options, mapState.zoom);
if (this.options.enableStroke) offscreenContext.fill();
if (this.options.enableFill) offscreenContext.stroke();
}
applyContextStyles(context, options, zoom) {
context.fillStyle = options.fillStyle;
context.strokeStyle = options.strokeStyle;
context.lineWidth = options.lineWidth * zoom;
context.setLineDash(options.lineDash);
context.lineJoin = 'round';
}
mapGeometry(pointCallback) {
return this.projectedGeometry.map((item) =>
item.geometry.map((list) =>
list.map(pointCallback)
)
);
}
projectGeometry(geometry, mapState) {
switch (geometry.type) {
case 'Polygon':
return [geometry.coordinates[0].map(item => this.projectPoint(mapState, item[0], item[1]))];
case 'MultiPolygon':
return geometry.coordinates.map(coordinates =>
coordinates[0].map(item => this.projectPoint(mapState, item[0], item[1]))
);
}
return [];
}
projectPoint(mapState, x, y) {
const position = TileConversion.latLonToPixel(
[y, x],
null,
mapState.zoom,
mapState.tileSize
);
return [-position[0], -position[1]];
}
}

@@ -23,2 +23,12 @@ export default class Tile {

}
isValid() {
const max = (1 << this.zoom);
if (this.x >= max || this.x < 0 || this.y >= max || this.y < 0) {
return false;
}
return true;
}
}

@@ -59,9 +59,13 @@ // Based on https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#ECMAScript_.28JavaScript.2FActionScript.2C_etc..29

static latLonToPixel(coord, center, zoom, tileSize, mapDimensions) {
static latLonToPixel(coord, center, zoom, tileSize) {
const tileX = TileConversion.lon2tile(coord[1], zoom, false);
const tileY = TileConversion.lat2tile(coord[0], zoom, false);
const tileCenterX = TileConversion.lon2tile(center[1], zoom, false);
const tileCenterY = TileConversion.lat2tile(center[0], zoom, false);
let tileCenterX = 0, tileCenterY = 0;
if (center) {
tileCenterX = TileConversion.lon2tile(center[1], zoom, false);
tileCenterY = TileConversion.lat2tile(center[0], zoom, false);
}
return [

@@ -68,0 +72,0 @@ -(tileX - tileCenterX) * tileSize,

@@ -76,5 +76,7 @@ import TileConversion from './TileConversion';

if (tileX >= 0 && tileY >= 0) {
grid[x][y] = new Tile(tileX, tileY, Math.round(this.tilesZoomLevel || options.zoom));
this.ensureTileAsset(grid[x][y]);
const tile = new Tile(tileX, tileY, Math.round(this.tilesZoomLevel || options.zoom));
if (tile.isValid()) {
this.ensureTileAsset(tile);
grid[x][y] = tile;
}

@@ -185,4 +187,3 @@ }

const totalTiles = horizontalTiles * verticalTiles;
let loadedTiles = 0;
let totalTiles = 0, loadedTiles = 0;

@@ -193,3 +194,9 @@ for (let y = 0; y < verticalTiles; y++) {

if (this.map.state.tiles[tile.id].loaded) {
if (tile) {
totalTiles++;
}
const cachedTile = tile && this.map.state.tiles[tile.id];
if (cachedTile && cachedTile.loaded) {
loadedTiles++;

@@ -196,0 +203,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc