mirror of
https://github.com/ConnectedHumber/Air-Quality-Web
synced 2024-12-22 10:25:01 +00:00
Port SVGWriter to javascript
This commit is contained in:
parent
233d780123
commit
a8444fdde6
5 changed files with 530 additions and 41 deletions
197
client_src/js/Helpers/SVGWriter.mjs
Normal file
197
client_src/js/Helpers/SVGWriter.mjs
Normal file
|
@ -0,0 +1,197 @@
|
|||
"use strict";
|
||||
|
||||
import XMLWriter from 'xml-writer';
|
||||
|
||||
import Vector2 from './Vector2.mjs';
|
||||
|
||||
/*
|
||||
* Simplifies the process for creating an SVG dynamically.
|
||||
* Originally written for MusicBoxConverter, but lifted, reused, and extended for FloatingIslands.
|
||||
* Ported from C# to Javascript for AirQualityWeb.
|
||||
* @license MPL-2.0
|
||||
*/
|
||||
class SvgWriter {
|
||||
constructor(viewBox) {
|
||||
// string outputFilename, string widthspec = "100%", string heightspec = "100%", Rectangle? rawViewBox = null
|
||||
this.unitSuffix = "";
|
||||
|
||||
// ----------------------------------
|
||||
|
||||
this.xml = new XMLWriter();
|
||||
|
||||
this.xml.startDocument();
|
||||
this.xml.writeDocType("svg", "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", null);
|
||||
this.xml.writeComment("Generated by SVGWriter which was written by Starbeamrainbowlabs");
|
||||
this.xml.startElement("svg", "http://www.w3.org/2000/svg");
|
||||
this.xml.writeAttribute("version", "1.1");
|
||||
this.xml.writeAttribute("x", "0");
|
||||
this.xml.writeAttribute("y", "0");
|
||||
this.xml.writeAttribute("width", widthspec);
|
||||
this.xml.writeAttribute("height", heightspec);
|
||||
|
||||
if (rawViewBox != null) {
|
||||
this.xml.writeAttribute(
|
||||
"viewBox",
|
||||
`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the SVG image currently being generated and closes the underlying file stream.
|
||||
* @return {this}
|
||||
*/
|
||||
complete() {
|
||||
this.xml.endElement();
|
||||
this.xml.endDocument();
|
||||
return this;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.xml.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a line to the image.
|
||||
* @param {Vector2} start The start position of the line.
|
||||
* @param {Vector2} end The end position of the line.
|
||||
* @param {String} [strokeStyle="darkgreen"] The colour to draw the line.
|
||||
* @param {Number} [strokeWidth=3] The width to draw the line.
|
||||
*/
|
||||
addLine(start, end, strokeStyle = "darkgreen", strokeWidth = 3) {
|
||||
this.xml.startElement("line");
|
||||
this.xml.writeAttribute("x1", `${start.X}${UnitSuffix}`);
|
||||
this.xml.writeAttribute("y1", `${start.Y}${UnitSuffix}`);
|
||||
this.xml.writeAttribute("x2", `${end.X}${UnitSuffix}`);
|
||||
this.xml.writeAttribute("y2", `${end.Y}${UnitSuffix}`);
|
||||
this.xml.writeAttribute("stroke", strokeStyle);
|
||||
this.xml.writeAttribute("stroke-width", strokeWidth.toString());
|
||||
this.xml.endElement();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new SVG group.
|
||||
* @param {string} [classes=null] The class(es) to apply to the new group.
|
||||
* @param {string} [transform=null] The transform(s) to apply to the new group.
|
||||
* @return {this}
|
||||
*/
|
||||
startGroup(classes = null, transform = null) {
|
||||
this.xml.startElement("g");
|
||||
if(classes != null)
|
||||
this.xml.writeAttribute("class", classes);
|
||||
if (transform != null)
|
||||
this.xml.writeAttribute("transform", transform);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Ends the most recently created unclosed group.
|
||||
* @return {this}
|
||||
*/
|
||||
endGroup() {
|
||||
this.xml.endElement();
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="scale"></param>
|
||||
/**
|
||||
* Starts a scale transform.
|
||||
* @param {number} scale The scale to enlarge (or shrink!) the next items by.
|
||||
* @return {this}
|
||||
*/
|
||||
startScaleTransform(scale)
|
||||
{
|
||||
this.xml.startElement("g");
|
||||
this.xml.writeAttribute("transform", `scale(${scale})`);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the most recently created scale transform.
|
||||
* @return {this}
|
||||
*/
|
||||
endTransform() {
|
||||
this.xml.endElement();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a hollow rectangle to the image.
|
||||
* @param {Vector2} position The position of the rectangle.
|
||||
* @param {Vector2} size The size of the rectangle.
|
||||
* @param {string} strokeStyle The colour to use when drawing.
|
||||
* @param {float} strokeWidth The line width to use when drawing.
|
||||
* @return {this}
|
||||
*/
|
||||
addRectangle(position, size, strokeStyle = "red", strokeWidth = 3) {
|
||||
this.xml.startElement("rect");
|
||||
this.xml.writeAttribute("x", `${position.x}${this.unitSuffix}`);
|
||||
this.xml.writeAttribute("y", `${position.y}${this.unitSuffix}`);
|
||||
this.xml.writeAttribute("width", `${size.X}${this.unitSuffix}`);
|
||||
this.xml.writeAttribute("height", `${size.Y}${this.unitSuffix}`);
|
||||
this.xml.writeAttribute("fill", "none");
|
||||
this.xml.writeAttribute("stroke", strokeStyle);
|
||||
this.xml.writeAttribute("stroke-width", strokeWidth.toString());
|
||||
this.xml.endElement();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Adds a circle to the image.
|
||||
* @param {Vector2} centre The position of the centre of the circle.
|
||||
* @param {Number} radius The radius of the circle.
|
||||
* @param {String} [fillStyle="blue"] The colour to fill the circle with.
|
||||
* @return {this}
|
||||
*/
|
||||
addCircle(centre, radius, fillStyle = "blue") {
|
||||
this.xml.startElement("circle");
|
||||
this.xml.writeAttribute("cx", `${centre.x}${this.unitSuffix}`);
|
||||
this.xml.writeAttribute("cy", `${centre.y}${this.unitSuffix}`);
|
||||
this.xml.writeAttribute("r", `${radius}${this.unitSuffix}`);
|
||||
this.xml.writeAttribute("fill", fillStyle);
|
||||
this.xml.endElement();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a solid n-sided polygon to the image.
|
||||
* @param {string} fillStyle The colour to fill the polygon with.
|
||||
* @param {Vector2[]} points The co-ordinates that make up the polygon.
|
||||
* @return {this}
|
||||
*/
|
||||
addPolygon(fillStyle, points)
|
||||
{
|
||||
this.xml.startElement("polygon");
|
||||
this.xml.writeAttribute("fill", fillStyle);
|
||||
this.xml.writeAttribute(
|
||||
"points",
|
||||
points.map((point) => `${point.x}${point.y}`).join(" ")
|
||||
);
|
||||
this.xml.endElement();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an isosceles / equilateral triangle to the image.
|
||||
* This is a shorthand method that calls AddPolygon() under-the-hood.
|
||||
* @param {Vector2} position The position to draw the triangle at.
|
||||
* @param {Number} baseWidth The width of the triangle's base.
|
||||
* @param {Number} height The height of the triangle.
|
||||
* @param {Boolean} upsideDown If set to true, then the triangle will point downwards instead of upwards.
|
||||
* @param {string} fillStyle The colour to fill the triangle with.
|
||||
* @return {this}
|
||||
*/
|
||||
addTriangleRegular(position, baseWidth, height, upsideDown, fillStyle)
|
||||
{
|
||||
this.addPolygon(
|
||||
fillStyle,
|
||||
position.subtract(new Vector2(baseWidth / 2, 0)),
|
||||
position.add(new Vector2(baseWidth / 2, 0)),
|
||||
position.subtract(new Vector2(0, upsideDown ? -height : height))
|
||||
);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default SvgWriter;
|
316
client_src/js/Helpers/Vector2.mjs
Normal file
316
client_src/js/Helpers/Vector2.mjs
Normal file
|
@ -0,0 +1,316 @@
|
|||
"use strict";
|
||||
|
||||
/******************************************************
|
||||
************** Simple ES6 Vector Class **************
|
||||
******************************************************
|
||||
* Author: Starbeamrainbowlabs
|
||||
* Twitter: @SBRLabs
|
||||
* Email: feedback at starbeamrainbowlabs dot com
|
||||
*
|
||||
* From https://gist.github.com/sbrl/69a8fa588865cacef9c0
|
||||
******************************************************
|
||||
* Originally written for my 2D Graphics ACW at Hull
|
||||
* University.
|
||||
******************************************************
|
||||
* Changelog
|
||||
******************************************************
|
||||
* 19th December 2015:
|
||||
* Added this changelog.
|
||||
* 28th December 2015:
|
||||
* Rewrite tests with klud.js + Node.js
|
||||
* 30th January 2016:
|
||||
* Tweak angleFrom function to make it work properly.
|
||||
* 31st January 2016:
|
||||
* Add the moveTowards function.
|
||||
* Add the minComponent getter.
|
||||
* Add the maxComponent getter.
|
||||
* Add the equalTo function.
|
||||
* Tests still need to be written for all of the above.
|
||||
* 19th September 2016:
|
||||
* Added Vector support to the multiply method.
|
||||
* 10th June 2017:
|
||||
* Fixed a grammatical mistake in a comment.
|
||||
* Added Vector.fromBearing static method.
|
||||
* 21st October 2017:
|
||||
* Converted to ES6 module.
|
||||
* Added Vector.zero and Vector.one constants. Remember to clone them!
|
||||
* 4th August 2018: (#LOWREZJAM!)
|
||||
* Optimised equalTo()
|
||||
* 6th August 2018: (#LOWREZJAM again!)
|
||||
* Added round(), floor(), and ceil()
|
||||
* 7th August 2018: (moar #LOWREZJAM :D)
|
||||
* Added area() and snapTo(grid_size)
|
||||
* 10th August 2018: (even more #LOWREZJAM!)
|
||||
* Added Vector support to divide()
|
||||
*/
|
||||
|
||||
class Vector {
|
||||
// Constructor
|
||||
constructor(inX, inY) {
|
||||
if(typeof inX != "number")
|
||||
throw new Error("Invalid x value.");
|
||||
if(typeof inY != "number")
|
||||
throw new Error("Invalid y value.");
|
||||
|
||||
// Store the (x, y) coordinates
|
||||
this.x = inX;
|
||||
this.y = inY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another vector to this vector.
|
||||
* @param {Vector} v The vector to add.
|
||||
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||||
*/
|
||||
add(v) {
|
||||
this.x += v.x;
|
||||
this.y += v.y;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take another vector from this vector.
|
||||
* @param {Vector} v The vector to subtrace from this one.
|
||||
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||||
*/
|
||||
subtract(v) {
|
||||
this.x -= v.x;
|
||||
this.y -= v.y;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide the current vector by a given value.
|
||||
* @param {Number|Vector} value The number (or Vector) to divide by.
|
||||
* @return {Vector} The current vector. Useful for daisy-chaining calls.
|
||||
*/
|
||||
divide(value) {
|
||||
if(value instanceof Vector)
|
||||
{
|
||||
this.x /= value.x;
|
||||
this.y /= value.y;
|
||||
}
|
||||
else if(typeof value == "number")
|
||||
{
|
||||
this.x /= value;
|
||||
this.y /= value;
|
||||
}
|
||||
else
|
||||
throw new Error("Can't divide by non-number value.");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the current vector by a given value.
|
||||
* @param {Number|Vector} value The number (or Vector) to multiply the current vector by.
|
||||
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||||
*/
|
||||
multiply(value) {
|
||||
if(value instanceof Vector)
|
||||
{
|
||||
this.x *= value.x;
|
||||
this.y *= value.y;
|
||||
}
|
||||
else if(typeof value == "number")
|
||||
{
|
||||
this.x *= value;
|
||||
this.y *= value;
|
||||
}
|
||||
else
|
||||
throw new Error("Can't multiply by non-number value.");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the vector towards the given vector by the given amount.
|
||||
* @param {Vector} v The vector to move towards.
|
||||
* @param {Number} amount The distance to move towards the given vector.
|
||||
*/
|
||||
moveTowards(v, amount)
|
||||
{
|
||||
// From http://stackoverflow.com/a/2625107/1460422
|
||||
var dir = new Vector(
|
||||
v.x - this.x,
|
||||
v.y - this.y
|
||||
).limitTo(amount);
|
||||
this.x += dir.x;
|
||||
this.y += dir.y;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the x and y components of this vector down to the next integer.
|
||||
* @return {Vector} This vector - useful for diasy-chaining.
|
||||
*/
|
||||
floor() {
|
||||
this.x = Math.floor(this.x);
|
||||
this.y = Math.floor(this.y);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Rounds the x and y components of this vector up to the next integer.
|
||||
* @return {Vector} This vector - useful for diasy-chaining.
|
||||
*/
|
||||
ceil() {
|
||||
this.x = Math.ceil(this.x);
|
||||
this.y = Math.ceil(this.y);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Rounds the x and y components of this vector to the nearest integer.
|
||||
* @return {Vector} This vector - useful for diasy-chaining.
|
||||
*/
|
||||
round() {
|
||||
this.x = Math.round(this.x);
|
||||
this.y = Math.round(this.y);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the 'area' of this vector and returns the result.
|
||||
* In other words, returns x * y. Useful if you're using a Vector to store
|
||||
* a size.
|
||||
* @return {Number} The 'area' of this vector.
|
||||
*/
|
||||
area() {
|
||||
return this.x * this.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Snaps this vector to an imaginary square grid with the specified sized
|
||||
* squares.
|
||||
* @param {Number} grid_size The size of the squares on the imaginary grid to which to snap.
|
||||
* @return {Vector} The current vector - useful for daisy-chaining.
|
||||
*/
|
||||
snapTo(grid_size) {
|
||||
this.x = Math.floor(this.x / grid_size) * grid_size;
|
||||
this.y = Math.floor(this.y / grid_size) * grid_size;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the length of the current vector to value without changing the
|
||||
* direction in which the vector is pointing.
|
||||
* @param {Number} value The number to limit the current vector's length to.
|
||||
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||||
*/
|
||||
limitTo(value) {
|
||||
if(typeof value != "number")
|
||||
throw new Error("Can't limit to non-number value.");
|
||||
|
||||
this.divide(this.length);
|
||||
this.multiply(value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dot product of the current vector and another vector.
|
||||
* @param {Vector} v The other vector we should calculate the dot product with.
|
||||
* @return {Vector} The current vector. Useful for daisy-chaining calls.
|
||||
*/
|
||||
dotProduct(v) {
|
||||
return (this.x * v.x) + (this.y * v.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the angle, in radians, from north to another vector.
|
||||
* @param {Vector} v The other vector to which to calculate the angle.
|
||||
* @return {Vector} The current vector. useful for daisy-chaining calls.
|
||||
*/
|
||||
angleFrom(v) {
|
||||
// From http://stackoverflow.com/a/16340752/1460422
|
||||
var angle = Math.atan2(v.y - this.y, v.x - this.x) - (Math.PI / 2);
|
||||
angle += Math.PI/2;
|
||||
if(angle < 0) angle += Math.PI * 2;
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the current vector.
|
||||
* @return {Vector} A clone of the current vector. Very useful for passing around copies of a vector if you don't want the original to be altered.
|
||||
*/
|
||||
clone() {
|
||||
return new Vector(this.x, this.y);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a representation of the current vector as a string.
|
||||
* @returns {string} A representation of the current vector as a string.
|
||||
*/
|
||||
toString() {
|
||||
return `(${this.x}, ${this.y})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the vector is equal to another vector.
|
||||
* @param {Vector} v The vector to compare to.
|
||||
* @return {boolean} Whether the current vector is equal to the given vector.
|
||||
*/
|
||||
equalTo(v)
|
||||
{
|
||||
return this.x == v.x && this.y == v.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unit vector of the current vector - that is a vector poiting in the same direction with a length of 1. Note that this does *not* alter the original vector.
|
||||
* @return {Vector} The current vector's unit form.
|
||||
*/
|
||||
get unitVector() {
|
||||
var length = this.length;
|
||||
return new Vector(
|
||||
this.x / length,
|
||||
this.y / length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of the current vector.
|
||||
* @return {Number} The length of the current vector.
|
||||
*/
|
||||
get length() {
|
||||
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the minimum component of the vector.
|
||||
* @return {Number} The minimum component of the vector.
|
||||
*/
|
||||
get minComponent() {
|
||||
return Math.min(this.x, this.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the maximum component of the vector.
|
||||
* @return {Number} The maximum component of the vector.
|
||||
*/
|
||||
get maxComponent() {
|
||||
return Math.min(this.x, this.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new vector based on an angle and a length.
|
||||
* @param {Number} angle The angle, in radians.
|
||||
* @param {Number} length The length.
|
||||
* @return {Vector} A new vector that represents the (x, y) of the specified angle and length.
|
||||
*/
|
||||
Vector.fromBearing = function(angle, length) {
|
||||
return new Vector(
|
||||
length * Math.cos(angle),
|
||||
length * Math.sin(angle)
|
||||
);
|
||||
}
|
||||
|
||||
Vector.zero = new Vector(0, 0);
|
||||
Vector.one = new Vector(1, 1);
|
||||
|
||||
export default Vector;
|
9
client_src/js/Overlay/VoronoiOverlay.mjs
Normal file
9
client_src/js/Overlay/VoronoiOverlay.mjs
Normal file
|
@ -0,0 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
class VoronoiOverlay {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default VoronoiOverlay;
|
44
package-lock.json
generated
44
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "air-quality-mapper",
|
||||
"version": "0.1.0",
|
||||
"version": "0.9.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1367,12 +1367,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"heatmap.js": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/heatmap.js/-/heatmap.js-2.0.5.tgz",
|
||||
"integrity": "sha1-Rm07hlE/XUkRKknSVwCrJzAUkVM=",
|
||||
"dev": true
|
||||
},
|
||||
"hex-color-regex": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||
|
@ -1711,37 +1705,6 @@
|
|||
"resolved": "https://registry.npmjs.org/leaflet-fullscreen/-/leaflet-fullscreen-1.0.2.tgz",
|
||||
"integrity": "sha1-CcYcS6xF9jsu4Sav2H5c2XZQ/Bs="
|
||||
},
|
||||
"leaflet-heatmap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/leaflet-heatmap/-/leaflet-heatmap-1.0.0.tgz",
|
||||
"integrity": "sha1-g8wEJJ057ZFSauqn12ab2qhtax8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"heatmap.js": "*",
|
||||
"leaflet": "*"
|
||||
}
|
||||
},
|
||||
"leaflet-timedimension": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leaflet-timedimension/-/leaflet-timedimension-1.1.0.tgz",
|
||||
"integrity": "sha1-nuy9+SZqLvFKzoKC2x0b/VsVEAY=",
|
||||
"requires": {
|
||||
"iso8601-js-period": "0.2.0",
|
||||
"leaflet": "~0.7.4 || ~1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"iso8601-js-period": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/iso8601-js-period/-/iso8601-js-period-0.2.0.tgz",
|
||||
"integrity": "sha1-5XQP1OcguWCoXraF/CBSAWIcAYw="
|
||||
},
|
||||
"leaflet": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.0.3.tgz",
|
||||
"integrity": "sha1-H0AbmLRcgZITTGyNaWhiU4BQB8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"leaflet.markercluster": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.4.1.tgz",
|
||||
|
@ -4398,6 +4361,11 @@
|
|||
"resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
|
||||
"integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
|
||||
"dev": true
|
||||
},
|
||||
"xml-writer": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-writer/-/xml-writer-1.7.0.tgz",
|
||||
"integrity": "sha1-t28dWRwWomNOvbcDx729D9aBkGU="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,13 +23,12 @@
|
|||
"iso8601-js-period": "^0.2.1",
|
||||
"leaflet": "^1.4.0",
|
||||
"leaflet-fullscreen": "^1.0.2",
|
||||
"leaflet-timedimension": "^1.1.0",
|
||||
"leaflet.markercluster": "^1.4.1",
|
||||
"leaflet-heatmap": "^1.0.0",
|
||||
"moment": "^2.24.0",
|
||||
"nanomodal": "^5.1.1",
|
||||
"smartsettings": "^1.2.3",
|
||||
"tabs": "^0.2.0"
|
||||
"tabs": "^0.2.0",
|
||||
"xml-writer": "^1.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chart.js": "^2.7.51",
|
||||
|
|
Loading…
Reference in a new issue