From e82cf6d9426c60591272cf7ac27e9234c007732e Mon Sep 17 00:00:00 2001 From: matafokka Date: Sat, 5 Feb 2022 13:54:58 +0300 Subject: [PATCH 1/9] Better polygon statistics - Mean height now can be set separately from min and max heights. - Mean height widget is now a number. - Added button to get mean height from min and max heights. - For DEMs, mean height is calculated from all pixels, not only min and max values. --- ESRIGridParser.js | 24 ++++++++++++++++++------ GeoTIFFParser.js | 9 +++------ SynthPolygonLayer/DEM.js | 1 - SynthPolygonLayer/polygons.js | 30 +++++++++++++++++++++++++----- locales/English.js | 3 ++- locales/Russian.js | 3 ++- 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/ESRIGridParser.js b/ESRIGridParser.js index 2fa03e15..bac36132 100644 --- a/ESRIGridParser.js +++ b/ESRIGridParser.js @@ -226,13 +226,10 @@ class ESRIGridParser { continue; if (!this.polygonsStats[name]) - this.polygonsStats[name] = {min: Infinity, max: -Infinity} + this.polygonsStats[name] = ESRIGridParser.createStatsObject(); let stats = this.polygonsStats[name]; - if (pixelValue < stats.min) - stats.min = pixelValue; - if (pixelValue > stats.max) - stats.max = pixelValue; + ESRIGridParser.addToStats(pixelValue, stats); } } else if (!isSpace && !isLineBreak) this.value += symbol; @@ -268,7 +265,7 @@ class ESRIGridParser { copyStats(layer = undefined) { let l = this.layer || layer; if (!l) - throw new Error("Error, can't copy statistics? If you're using ESRIGridParser in a WebWorker, call this method outside of WebWorker!"); + throw new Error("Can't copy statistics. If you're using ESRIGridParser in a WebWorker, call this method outside of WebWorker!"); ESRIGridParser.copyStats(l, this.polygonsStats); this.clearState(); } @@ -340,12 +337,27 @@ class ESRIGridParser { for (let name in stats) { let widgetable = layer.polygonsWidgets[name]; let s = stats[name]; + s.mean = s.sum / s.count; widgetable.getWidgetById("minHeight").setValue(s.min); widgetable.getWidgetById("maxHeight").setValue(s.max); + widgetable.getWidgetById("meanHeight").setValue(s.mean); } layer.updateAll(); } + static createStatsObject() { + return {min: Infinity, max: -Infinity, mean: 0, sum: 0, count: 0} + } + + static addToStats(value, stats) { + if (value < stats.min) + stats.min = value; + if (value > stats.max) + stats.max = value; + stats.sum += value; + stats.count++; + } + } module.exports = ESRIGridParser; \ No newline at end of file diff --git a/GeoTIFFParser.js b/GeoTIFFParser.js index b0b39f4a..4f0d57bd 100644 --- a/GeoTIFFParser.js +++ b/GeoTIFFParser.js @@ -6,6 +6,7 @@ const intersect = require("@turf/intersect").default; const toProj4 = require("geotiff-geokeys-to-proj4"); const proj4 = require("proj4"); const MathTools = require("./MathTools.js"); +const ESRIGridParser = require("./ESRIGridParser.js"); /** * Parses GeoTIFF files @@ -79,7 +80,7 @@ module.exports = async function (file, projectionString, initialData) { // geotiff.js will mash all pixels into one array. // To easily keep track of coordinates and reduce memory consumption, we need to read image row by row. let [minX, currentY, maxX, maxY] = imageWindow, - stats = {min: Infinity, max: -Infinity} // Stats for current polygon + stats = ESRIGridParser.createStatsObject(); // Stats for current polygon for (currentY; currentY <= maxY; currentY++) { let currentX = minX; @@ -107,11 +108,7 @@ module.exports = async function (file, projectionString, initialData) { let multipliedValue = value * zScale; if (value === nodata || multipliedValue === nodata) continue; - - if (multipliedValue < stats.min) - stats.min = multipliedValue; - if (multipliedValue > stats.max) - stats.max = multipliedValue; + ESRIGridParser.addToStats(multipliedValue, stats); } } polygonStats[name] = stats; diff --git a/SynthPolygonLayer/DEM.js b/SynthPolygonLayer/DEM.js index 070db48d..b1980aa4 100644 --- a/SynthPolygonLayer/DEM.js +++ b/SynthPolygonLayer/DEM.js @@ -129,7 +129,6 @@ L.ALS.SynthPolygonLayer.prototype.onDEMLoadWorker = async function (widget) { }); }); } - }; L.ALS.SynthPolygonLayer.prototype.getFileBaseName = function (filename) { diff --git a/SynthPolygonLayer/polygons.js b/SynthPolygonLayer/polygons.js index a74bed0a..fcd7b84c 100644 --- a/SynthPolygonLayer/polygons.js +++ b/SynthPolygonLayer/polygons.js @@ -1,10 +1,29 @@ +/** + * Serializable button handler for getting mean height from min and max heights + * + * @class + * @extends L.ALS.Serializable + */ +L.ALS.MeanHeightButtonHandler = L.ALS.Serializable.extend( /**@lends L.ALS.MeanHeightButtonHandler.prototype */ { + + initialize: function (controlsContainer) { + this._widgetable = controlsContainer; + }, + + handle: function () { + this._widgetable.getWidgetById("meanHeight").setValue( + (this._widgetable.getWidgetById("minHeight").getValue() + this._widgetable.getWidgetById("maxHeight").getValue()) / 2 + ); + } +}) + L.ALS.SynthPolygonLayer.prototype.addPolygon = function (polygon) { polygon._intName = this._generatePolygonName(polygon); polygon.setStyle({fill: true}); this.polygons[polygon._intName] = polygon; - let controlsContainer = new L.WidgetLayer(polygon.getLatLngs()[0][1], "topLeft"); + let controlsContainer = new L.WidgetLayer(polygon.getLatLngs()[0][1], "topLeft"), handler = new L.ALS.MeanHeightButtonHandler(controlsContainer); if (this.useZoneNumbers) controlsContainer.addWidget(new L.ALS.Widgets.Number("zoneNumber", "zoneNumber", this, "_calculatePolygonParameters").setMin(1).setValue(1)); @@ -12,7 +31,8 @@ L.ALS.SynthPolygonLayer.prototype.addPolygon = function (polygon) { controlsContainer.addWidgets( new L.ALS.Widgets.Number("minHeight", "minHeight", this, "_calculatePolygonParameters").setMin(1).setValue(1), new L.ALS.Widgets.Number("maxHeight", "maxHeight", this, "_calculatePolygonParameters").setMin(1).setValue(1), - new L.ALS.Widgets.ValueLabel("meanHeight", "meanHeight", "m"), + new L.ALS.Widgets.Number("meanHeight", "meanHeight", this, "_calculatePolygonParameters").setMin(1).setValue(1), + new L.ALS.Widgets.Button("meanFromMinMax", "meanFromMinMax", handler, "handle"), new L.ALS.Widgets.ValueLabel("absoluteHeight", "absoluteHeight", "m"), new L.ALS.Widgets.ValueLabel("elevationDifference", "elevationDifference"), new L.ALS.Widgets.ValueLabel("reliefType", "reliefType"), @@ -21,7 +41,7 @@ L.ALS.SynthPolygonLayer.prototype.addPolygon = function (polygon) { new L.ALS.Widgets.ValueLabel("latCellSizeInMeters", "latCellSizeInMeters", "m").setNumberOfDigitsAfterPoint(0), ); - let toFormatNumbers = ["meanHeight", "absoluteHeight", "elevationDifference", "lngCellSizeInMeters", "latCellSizeInMeters"]; + let toFormatNumbers = ["absoluteHeight", "elevationDifference", "lngCellSizeInMeters", "latCellSizeInMeters"]; for (let id of toFormatNumbers) controlsContainer.getWidgetById(id).setFormatNumbers(true); @@ -37,7 +57,7 @@ L.ALS.SynthPolygonLayer.prototype.removePolygon = function (polygon, removeFromO delete this.polygonsWidgets[name]; } -L.ALS.SynthPolygonLayer.prototype._calculatePolygonParameters = function () { +L.ALS.SynthPolygonLayer.prototype._calculatePolygonParameters = function (widget) { this.selectedArea = 0; for (let name in this.polygons) { if (!this.polygons.hasOwnProperty(name)) @@ -64,7 +84,7 @@ L.ALS.SynthPolygonLayer.prototype._calculatePolygonParameters = function () { } errorLabel.setValue(""); - layer.meanHeight = Math.round((layer.maxHeight + layer.minHeight) / 2); + layer.meanHeight = widgetContainer.getWidgetById("meanHeight").getValue(); layer.absoluteHeight = this["flightHeight"] + layer.meanHeight; layer.elevationDifference = (layer.maxHeight - layer.minHeight) / this["flightHeight"]; diff --git a/locales/English.js b/locales/English.js index 750418b1..5445699e 100644 --- a/locales/English.js +++ b/locales/English.js @@ -74,7 +74,8 @@ L.ALS.Locales.addLocaleProperties("English", { zoneNumber: "Zone number", minHeight: "Min. height (m):", maxHeight: "Max. height (m):", - meanHeight: "Mean height", + meanHeight: "Mean height (m)", + meanFromMinMax: "Get mean height from min. and max. heights", absoluteHeight: "Absolute height", elevationDifference: "(Max. height - Min. height) / Flight height", reliefType: "Relief type", diff --git a/locales/Russian.js b/locales/Russian.js index d818044b..adf8b359 100644 --- a/locales/Russian.js +++ b/locales/Russian.js @@ -69,7 +69,8 @@ L.ALS.Locales.addLocaleProperties("Русский", { zoneNumber: "Номер участка", minHeight: "Мин. высота (m):", maxHeight: "Макс. высота (m):", - meanHeight: "Ср. высота", + meanHeight: "Ср. высота (m)", + meanFromMinMax: "Вычислить ср. высоту из мин. и макс. высот", absoluteHeight: "Абс. высота", elevationDifference: "(Макс. высота - Мин. высота) / Высота полета", reliefType: "Тип рельефа", From 9505f34bdb71ed44a812755a8f0c978137f61a28 Mon Sep 17 00:00:00 2001 From: matafokka Date: Sun, 6 Feb 2022 14:53:34 +0300 Subject: [PATCH 2/9] Code quality improvements. Kinda. - Added missing important JSDoc annotations. - Finally moved Leaflet import to the main script. - Removed unused files from the build. --- SynthBase/SynthBaseLayer.js | 45 ++++++++++++++++++++++++ SynthBase/calculateParameters.js | 1 - SynthGeometryLayer/SynthGeometryLayer.js | 2 +- SynthGridLayer/SynthGridLayer.js | 2 +- SynthLineLayer/SynthLineLayer.js | 8 ++++- SynthPolygonLayer/SynthPolygonLayer.js | 3 ++ build.js | 2 -- index.html | 1 - main.js | 3 +- 9 files changed, 59 insertions(+), 8 deletions(-) diff --git a/SynthBase/SynthBaseLayer.js b/SynthBase/SynthBaseLayer.js index da1b3669..20d42293 100644 --- a/SynthBase/SynthBaseLayer.js +++ b/SynthBase/SynthBaseLayer.js @@ -2,6 +2,14 @@ require("./SynthBaseSettings.js"); const turfHelpers = require("@turf/helpers"); const MathTools = require("../MathTools.js"); +/** + * @typedef {Object} PathData + * @property {L.FeatureGroup} pathGroup Group with the path + * @property {L.FeatureGroup} connectionsGroup Group with the connections + * @property {string} colorLabel Label for the color input + * @property {L.Layer[]} toUpdateColors Layers to update colors of + */ + /** * Base layer. Provides airport markers, basic calculations and menu entries for them. * @@ -30,6 +38,11 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot init: function (settings, pathGroup1, connectionsGroup1, colorLabel1, path1AdditionalLayers = [], pathGroup2 = undefined, connectionsGroup2 = undefined, colorLabel2 = undefined, path2AdditionalLayers = []) { + /** + * Settings passed from ALS + * @type {Object} + * @private + */ this._settings = settings; /** @@ -39,6 +52,10 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot */ this._pathsWidgetsNumber = 1; + /** + * Data related to the first path + * @type PathData + */ this.path1 = { pathGroup: pathGroup1, connectionsGroup: connectionsGroup1, @@ -46,6 +63,10 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot toUpdateColors: [pathGroup1, connectionsGroup1, ...path1AdditionalLayers] } + /** + * Data related to the second path + * @type PathData + */ this.path2 = pathGroup2 ? { pathGroup: pathGroup2, connectionsGroup: connectionsGroup2, @@ -53,9 +74,18 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot toUpdateColors: [pathGroup2, connectionsGroup2, ...path2AdditionalLayers] } : undefined; + /** + * Indicates whether this layer has Y overlay, i.e. if it has parallel paths + * @type {boolean} + */ this.hasYOverlay = !!this.path2; + /** + * Array of paths to work with + * @type {PathData[]} + */ this.paths = [this.path1]; + if (this.hasYOverlay) this.paths.push(this.path2); @@ -90,6 +120,10 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot html: "
" }) + /** + * Airport marker + * @protected + */ this._airportMarker = L.marker(this.map.getCenter(), { icon: icon, draggable: true @@ -100,6 +134,9 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot this.addLayers(this._airportMarker); }, + /** + * Adds basic parameters' widgets to the menu. Should be called at the constructor! + */ addBaseParametersInputSection: function () { this.addWidget( new L.ALS.Widgets.Number("lineThickness", "lineThickness", this, "setLineThickness").setMin(1).setMax(20).setValue(this._settings.lineThicknessValue), @@ -138,6 +175,9 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot this._airportMarker.fire("drag"); // Just to set values }, + /** + * Adds basic parameters' widgets to the menu. Should be called at the constructor! + */ addBaseParametersOutputSection: function () { let yWidgets = []; if (this.hasYOverlay) @@ -186,6 +226,10 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot this.updateDrawThickness(); }, + /** + * Sets paths' colors, i.e. colors of layers in {@link PathData.toUpdateColors} + * @private + */ _setPathsColor: function () { for (let i = 0; i < this.paths.length; i++) { let style = {color: this.getWidgetById(`color${i}`).getValue()}, @@ -195,6 +239,7 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot } }, + setAirportLatLng: function () { this._airportMarker.setLatLng([ this.getWidgetById("airportLat").getValue(), diff --git a/SynthBase/calculateParameters.js b/SynthBase/calculateParameters.js index 3294aed6..4b7bca65 100644 --- a/SynthBase/calculateParameters.js +++ b/SynthBase/calculateParameters.js @@ -35,7 +35,6 @@ L.ALS.SynthBaseLayer.prototype.calculateParameters = function () { let names = ["flightHeight", "lx", "Lx", "Bx", "ly", "Ly", "GSI", "IFOV", "GIFOV", "FOV", "GFOV", "timeBetweenCaptures"]; - if (this.hasYOverlay) { this.By = this.Ly * (100 - this["overlayBetweenPaths"]) / 100; // Distance between paths names.push("By"); diff --git a/SynthGeometryLayer/SynthGeometryLayer.js b/SynthGeometryLayer/SynthGeometryLayer.js index 46842ec8..b037dea4 100644 --- a/SynthGeometryLayer/SynthGeometryLayer.js +++ b/SynthGeometryLayer/SynthGeometryLayer.js @@ -3,7 +3,7 @@ require("./SynthGeometrySettings.js"); const shp = require("shpjs"); /** - * Shapefile layer + * Layer with geometry from shapefile or GeoJSON * @class * @extends L.ALS.Layer */ diff --git a/SynthGridLayer/SynthGridLayer.js b/SynthGridLayer/SynthGridLayer.js index ba57e785..632dedec 100644 --- a/SynthGridLayer/SynthGridLayer.js +++ b/SynthGridLayer/SynthGridLayer.js @@ -6,7 +6,7 @@ require("./SynthGridWizard.js"); * @class * @extends L.ALS.Layer */ -L.ALS.SynthGridLayer = L.ALS.SynthPolygonLayer.extend({ +L.ALS.SynthGridLayer = L.ALS.SynthPolygonLayer.extend(/** @lends L.ALS.SynthGridLayer.prototype */{ defaultName: "Grid Layer", useZoneNumbers: true, diff --git a/SynthLineLayer/SynthLineLayer.js b/SynthLineLayer/SynthLineLayer.js index 95c966fd..fabf09a6 100644 --- a/SynthLineLayer/SynthLineLayer.js +++ b/SynthLineLayer/SynthLineLayer.js @@ -3,7 +3,13 @@ require("./SynthLineSettings.js"); const turfHelpers = require("@turf/helpers"); const MathTools = require("../MathTools.js"); -L.ALS.SynthLineLayer = L.ALS.SynthBaseLayer.extend({ +/** + * Geodesic line layer + * + * @class + * @extends L.ALS.SynthBaseLayer + */ +L.ALS.SynthLineLayer = L.ALS.SynthBaseLayer.extend(/** @lends L.ALS.SynthLineLayer.prototype */{ defaultName: "Line Layer", hideCapturePoints: true, hidePathsConnections: false, diff --git a/SynthPolygonLayer/SynthPolygonLayer.js b/SynthPolygonLayer/SynthPolygonLayer.js index 4ee195ea..916eaa71 100644 --- a/SynthPolygonLayer/SynthPolygonLayer.js +++ b/SynthPolygonLayer/SynthPolygonLayer.js @@ -11,6 +11,9 @@ try { /** * Base layer for rectangle-based planning + * + * @class + * @extends L.ALS.SynthBaseLayer */ L.ALS.SynthPolygonLayer = L.ALS.SynthBaseLayer.extend( /** @lends L.ALS.SynthPolygonLayer.prototype */ { diff --git a/build.js b/build.js index 8420fc9e..80a8d51d 100644 --- a/build.js +++ b/build.js @@ -99,10 +99,8 @@ let toCopy = ["index.html", "img/logo.ico", "img/logo.svg", "img/logo.png", "img "node_modules/leaflet-advanced-layer-system/dist/css", "node_modules/leaflet-advanced-layer-system/dist/polyfills.js", "node_modules/leaflet/dist/leaflet.css", - "node_modules/leaflet/dist/leaflet.js", "node_modules/leaflet/dist/images", "node_modules/leaflet.coordinates/dist/Leaflet.Coordinates-0.1.5.css", - "node_modules/leaflet.coordinates/dist/Leaflet.Coordinates-0.1.5.min.js", "node_modules/leaflet-draw/dist/leaflet.draw.css", "node_modules/leaflet-draw/dist/images", ]; diff --git a/index.html b/index.html index 6dd2b30b..a7208895 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,6 @@ - diff --git a/main.js b/main.js index 0cf36c76..e959cc56 100644 --- a/main.js +++ b/main.js @@ -8,7 +8,8 @@ // I've spend hours struggling with this issue, so don't try to reorganise the code, you'll fail and, as it seems, break compatibility with the older Electron versions. //require("fastestsmallesttextencoderdecoder"); -L.Geodesic = require("leaflet.geodesic"); +window.L = require("leaflet"); +L.Geodesic = require("leaflet.geodesic").GeodesicLine; require("leaflet-draw"); require("./DrawGeodesic.js"); require("leaflet-advanced-layer-system"); From c247cc85ccbe70cbd89ad012279e124717d8b85b Mon Sep 17 00:00:00 2001 From: matafokka Date: Sun, 6 Feb 2022 15:56:10 +0300 Subject: [PATCH 3/9] First letter in cells' names now is not localized --- SynthGridLayer/onMapPan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SynthGridLayer/onMapPan.js b/SynthGridLayer/onMapPan.js index 6d69bbe9..9d07c33c 100644 --- a/SynthGridLayer/onMapPan.js +++ b/SynthGridLayer/onMapPan.js @@ -105,7 +105,7 @@ L.ALS.SynthGridLayer.prototype._onMapPan = function () { // 1:1 000 000. This part is always present let index = Math.floor(Math.abs(fixedLat) / 4); - let letter = L.ALS.locale.alphabet[index]; + let letter = L.ALS.Locales.English.alphabet[index]; let number = Math.floor(fixedLng / 6) + 31; let polygonName = letter + "-" + number; From 4b3f7e0b68c2ea4b8c27af266dc7b5039d5bea21 Mon Sep 17 00:00:00 2001 From: matafokka Date: Mon, 7 Feb 2022 16:22:51 +0300 Subject: [PATCH 4/9] External links fixes, about section changes - Fixed external links opening in the same tab or Electron window. - Now about section displays SynthFlight version. - Added links to the map providers in about section. Also changed list appearance. - Bumped ALS version. --- SynthBase/SynthBaseLayer.js | 2 +- about.js | 20 +++++++------------- electronApp.js | 8 +++++++- package.json | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/SynthBase/SynthBaseLayer.js b/SynthBase/SynthBaseLayer.js index 20d42293..50919d4d 100644 --- a/SynthBase/SynthBaseLayer.js +++ b/SynthBase/SynthBaseLayer.js @@ -476,7 +476,7 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot flashPath: function (widget) { // Close menu on mobile if (L.ALS.Helpers.isMobile) - L.ALS.Helpers.dispatchEvent(this._layerSystem._menuCloseButton, "click"); + this.layerSystem.clickOnMenu(); for (let group of widget.toFlash) { let layers = group instanceof L.FeatureGroup ? group.getLayers() : [group]; diff --git a/about.js b/about.js index a6153eff..8b74f813 100644 --- a/about.js +++ b/about.js @@ -1,3 +1,5 @@ +const {version} = require("./package.json"); + module.exports = ` -

LogoSynthFlight Alpha

+

LogoSynthFlight ${version}

-

+

diff --git a/electronApp.js b/electronApp.js index de6e8b84..309b57e8 100644 --- a/electronApp.js +++ b/electronApp.js @@ -1,4 +1,4 @@ -const { app, BrowserWindow } = require("electron"); +const { app, BrowserWindow, shell } = require("electron"); const remote = require("@electron/remote/main"); remote.initialize(); const integrate = require("leaflet-advanced-layer-system/ElectronIntegration"); @@ -28,6 +28,12 @@ function createWindow () { integrate(mainWindow, { useToolbarAsFrame: true, }); + + // Open links in a browser + mainWindow.webContents.setWindowOpenHandler(details => { + shell.openExternal(details.url); + return {action: "deny"} + }) } app.commandLine.appendSwitch("enable-experimental-web-platform-features"); diff --git a/package.json b/package.json index 7d2df645..2e9e94e3 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,6 @@ }, "dependencies": { "@electron/remote": "^2.0.1", - "leaflet-advanced-layer-system": "^2.1.6" + "leaflet-advanced-layer-system": "^2.1.7" } } From 58b9c2c7bd7eed1c2ab782dd06327d0db8425b09 Mon Sep 17 00:00:00 2001 From: matafokka Date: Mon, 7 Feb 2022 16:58:00 +0300 Subject: [PATCH 5/9] Fixed package.json being fully bundled --- build.js | 4 +++- package-lock.json | 49 ++++++++++++++++++++++++++++++++++++++--------- package.json | 1 + 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/build.js b/build.js index 80a8d51d..27c6245b 100644 --- a/build.js +++ b/build.js @@ -80,7 +80,9 @@ let mainFile = "main.js"; persistify({ entries: [mainFile], debug: debug -}).require("./node_modules/geotiff/src/geotiff.js", {expose: "geotiff"}) // Thanks to parcel for this nightmare +}) + .require("./node_modules/geotiff/src/geotiff.js", {expose: "geotiff"}) // Thanks to parcel for this nightmare + .transform("package-json-versionify") .transform("babelify", { presets: ["@babel/preset-env"], global: true, // ShpJS is built without polyfills and uses async functions. So we have to build node_modules too. Maybe other modules are built that way too. diff --git a/package-lock.json b/package-lock.json index 2ac1f7a3..cdf9ae7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "synthflight", - "version": "0.0.16-alpha", + "version": "0.0.17-alpha", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "synthflight", - "version": "0.0.16-alpha", + "version": "0.0.17-alpha", "hasInstallScript": true, "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.1", - "leaflet-advanced-layer-system": "^2.1.4" + "leaflet-advanced-layer-system": "^2.1.7" }, "devDependencies": { "@babel/core": "^7.12.3", @@ -43,6 +43,7 @@ "leaflet-draw": "^1.0.4", "leaflet.coordinates": "~0.1.5", "leaflet.geodesic": "^2.5.5-0", + "package-json-versionify": "^1.0.4", "persistify": "^2.0.1", "polybooljs": "^1.2.0", "postcss": "^8.2.4", @@ -2224,6 +2225,12 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/browserify-package-json": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-package-json/-/browserify-package-json-1.0.1.tgz", + "integrity": "sha1-mN3oqlxWH9bT/km7qhArdLOW/eo=", + "dev": true + }, "node_modules/browserify-rsa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", @@ -6022,9 +6029,9 @@ "dev": true }, "node_modules/leaflet-advanced-layer-system": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.4.tgz", - "integrity": "sha512-097PPwHzJ+oVOKV2ul2cMENj3TQ/d5ZFow1wH5n1uLIaDj7j9ZnCuIaRP6f5HO/WTvwaLjRpgUsA7yiO6AhsiQ==" + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.7.tgz", + "integrity": "sha512-xveypJvI5GvtgKcHtD+oVARRt5DYA5DmghpUlMX+7hnLwbVEt8ZjdtVW2LpLuvt905DREC9T8FJ4sHk6B5XuCw==" }, "node_modules/leaflet-draw": { "version": "1.0.4", @@ -6998,6 +7005,15 @@ "node": ">=4" } }, + "node_modules/package-json-versionify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/package-json-versionify/-/package-json-versionify-1.0.4.tgz", + "integrity": "sha1-WGBYepRIc6a35tJujlH/siMVvxc=", + "dev": true, + "dependencies": { + "browserify-package-json": "^1.0.0" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -14496,6 +14512,12 @@ "safe-buffer": "^5.1.2" } }, + "browserify-package-json": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-package-json/-/browserify-package-json-1.0.1.tgz", + "integrity": "sha1-mN3oqlxWH9bT/km7qhArdLOW/eo=", + "dev": true + }, "browserify-rsa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", @@ -17533,9 +17555,9 @@ "dev": true }, "leaflet-advanced-layer-system": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.4.tgz", - "integrity": "sha512-097PPwHzJ+oVOKV2ul2cMENj3TQ/d5ZFow1wH5n1uLIaDj7j9ZnCuIaRP6f5HO/WTvwaLjRpgUsA7yiO6AhsiQ==" + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.7.tgz", + "integrity": "sha512-xveypJvI5GvtgKcHtD+oVARRt5DYA5DmghpUlMX+7hnLwbVEt8ZjdtVW2LpLuvt905DREC9T8FJ4sHk6B5XuCw==" }, "leaflet-draw": { "version": "1.0.4", @@ -18333,6 +18355,15 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "package-json-versionify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/package-json-versionify/-/package-json-versionify-1.0.4.tgz", + "integrity": "sha1-WGBYepRIc6a35tJujlH/siMVvxc=", + "dev": true, + "requires": { + "browserify-package-json": "^1.0.0" + } + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", diff --git a/package.json b/package.json index 2e9e94e3..fd335a9d 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "leaflet-draw": "^1.0.4", "leaflet.coordinates": "~0.1.5", "leaflet.geodesic": "^2.5.5-0", + "package-json-versionify": "^1.0.4", "persistify": "^2.0.1", "polybooljs": "^1.2.0", "postcss": "^8.2.4", From e1d6d5b89b0836296936388116562bd50dfda678 Mon Sep 17 00:00:00 2001 From: matafokka Date: Mon, 7 Feb 2022 18:53:46 +0300 Subject: [PATCH 6/9] Hull connection improvements - Added lng wrapping to the monotone chain algorithm which should improve hull connection. - Removed TODO item. - Added missing semicolon. --- MathTools.js | 10 ++++++++++ SynthBase/Hull.js | 24 +++++------------------- SynthPolygonLayer/SynthPolygonLayer.js | 2 +- SynthPolygonLayer/drawPaths.js | 2 +- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/MathTools.js b/MathTools.js index afc2003c..134551f7 100644 --- a/MathTools.js +++ b/MathTools.js @@ -323,6 +323,16 @@ class MathTools { return {x: "lng", y: "lat"} } + /** + * Wraps lng2 to lng1 + * @param lng1 {number} Anchor lng + * @param lng2 {number} Lng to wrap + * @return {number} Wrapped lng2 + */ + static wrapLng(lng1, lng2) { + return lng2 - Math.round((lng2 - lng1) / 360); + } + } module.exports = MathTools; \ No newline at end of file diff --git a/SynthBase/Hull.js b/SynthBase/Hull.js index e6317453..22e912e3 100644 --- a/SynthBase/Hull.js +++ b/SynthBase/Hull.js @@ -169,9 +169,9 @@ L.ALS.SynthBaseLayer.prototype.buildHull = function (path, color) { * @return {{upper: L.LatLng[], lower: L.LatLng[]}} Convex hull of given points */ L.ALS.SynthBaseLayer.prototype.getConvexHull = function (points) { - // Find convex hull of the points using monotone chain. + // Find convex hull of the points using monotone chain points.sort((a, b) => { - return a.lng === b.lng ? a.lat - b.lat : a.lng - b.lng; + return MathTools.isEqual(a.lng, b.lng) ? a.lat - b.lat : a.lng - MathTools.wrapLng(a.lng, b.lng); }); let lower = []; @@ -392,22 +392,8 @@ L.ALS.SynthBaseLayer.prototype.getOrderedPathFromHull = function (prevPoint, con return copyTo[copyTo.length - 1]; // Return the last added point which will become a previous point for the next iteration } -/** - * Calculates length of a polyline - * @param line {L.Polyline | L.LatLng[] | number[][]} Line to calculate length of - * @return {number} Line length - */ -L.ALS.SynthBaseLayer.prototype.getLineLength = function (line) { - let latLngs = line instanceof Array ? line : line.getLatLngs(), length = 0, - {x, y} = MathTools.getXYPropertiesForPoint(latLngs[0]); - - for (let i = 0; i < latLngs.length - 1; i++) { - const p1 = latLngs[i], p2 = latLngs[i + 1]; - length += Math.sqrt((p1[x] - p2[x]) ** 2 + (p1[y] - p2[y]) ** 2); - } - return length; -} - L.ALS.SynthBaseLayer.prototype.cross = function (a, b, o) { - return (a.lng - o.lng) * (b.lat - o.lat) - (a.lat - o.lat) * (b.lng - o.lng); + // It calculates only direction, so it should be good enough. Also, we wrap lngs to get correct direction on + // lng differences > 90 and near map's edges + return (MathTools.wrapLng(o.lng, a.lng) - o.lng) * (b.lat - o.lat) - (a.lat - o.lat) * (MathTools.wrapLng(o.lng, b.lng) - o.lng); } \ No newline at end of file diff --git a/SynthPolygonLayer/SynthPolygonLayer.js b/SynthPolygonLayer/SynthPolygonLayer.js index 916eaa71..1b7fd619 100644 --- a/SynthPolygonLayer/SynthPolygonLayer.js +++ b/SynthPolygonLayer/SynthPolygonLayer.js @@ -131,7 +131,7 @@ L.ALS.SynthPolygonLayer = L.ALS.SynthBaseLayer.extend( /** @lends L.ALS.SynthPol this.getWidgetById("hideCapturePoints").callCallback(); }, - // TODO: I don't remember what this is for, use it or remove it + // It overrides parent method, my IDE can't see it getPathLength: function (layer) { // Basically, inverse of L.ALS.SynthBaseLayer#getArcAngleByLength let latLngs = layer instanceof Array ? layer : layer.getLatLngs(), length = 0; diff --git a/SynthPolygonLayer/drawPaths.js b/SynthPolygonLayer/drawPaths.js index 2c3c91f8..69da725a 100644 --- a/SynthPolygonLayer/drawPaths.js +++ b/SynthPolygonLayer/drawPaths.js @@ -40,7 +40,7 @@ L.ALS.SynthPolygonLayer.prototype._drawPaths = function () { if (this.mergedPolygons.length === 0) return; - this._drawPathsWorker(true) + this._drawPathsWorker(true); this._drawPathsWorker(false); this.updatePathsMeta(); this.labelsGroup.redraw(); From a30e2182321884f7d4665ab799e083734a09134a Mon Sep 17 00:00:00 2001 From: matafokka Date: Tue, 15 Feb 2022 14:23:59 +0300 Subject: [PATCH 7/9] Geodesic integration, bug fixes - Fully integrated my own fork of Leaflet.Geodesic with the new features. - Line layer seems to be working fine. Gotta check export and serialization tho. - Fixed connection to the airport not working. - Fixed widgets on the map not hiding properly. --- DrawGeodesic.js | 13 +-- SynthBase/SynthBaseLayer.js | 4 +- SynthGridLayer/onMapZoom.js | 8 +- SynthLineLayer/SynthLineLayer.js | 137 +++++-------------------------- main.js | 7 ++ package-lock.json | 13 ++- package.json | 2 +- 7 files changed, 50 insertions(+), 134 deletions(-) diff --git a/DrawGeodesic.js b/DrawGeodesic.js index 50a1eadf..a255753e 100644 --- a/DrawGeodesic.js +++ b/DrawGeodesic.js @@ -23,7 +23,11 @@ function _createGeodesic (coords, opts = {}) { return geodesic; } -function _polyRedraw(poly) { +/** + * Redraws Leaflet layer or geodesic line + * @param poly {L.Layer | L.Geodesic} Layer to redraw + */ +L.redrawLayer = function (poly) { if (poly.updateGeometry) poly.updateGeometry(); else @@ -321,7 +325,6 @@ L.Draw.Polyline = L.Draw.Feature.extend({ _shouldAbortMouseEvent: function (e) { let {lat, lng} = e.latlng; - console.log(e, !this.options.shapeOptions.naturalDrawing && (lat > 85 || lat < -85 || lng < -180 || lng > 180)) return !this.options.shapeOptions.naturalDrawing && (lat > 85 || lat < -85 || lng < -180 || lng > 180); }, @@ -864,7 +867,7 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ var latlngs = this._defaultShape(); var removed = [].splice.apply(latlngs, arguments); this._poly._convertLatLngs(latlngs, true); - _polyRedraw(this._poly); + L.redrawLayer(this._poly); return removed; }, @@ -939,7 +942,7 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ this._poly._bounds._northEast = L.latLng(-Infinity, -Infinity); var latlngs = this._poly.getLatLngs(); this._poly._convertLatLngs(latlngs, true); - _polyRedraw(this._poly); + L.redrawLayer(this._poly); this._poly.fire('editdrag'); }, @@ -1001,7 +1004,7 @@ L.Edit.PolyVerticesEdit = L.Handler.extend({ marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next)); } - _polyRedraw(this._poly); + L.redrawLayer(this._poly); this.updateMarkers(); }, diff --git a/SynthBase/SynthBaseLayer.js b/SynthBase/SynthBaseLayer.js index 50919d4d..3032f484 100644 --- a/SynthBase/SynthBaseLayer.js +++ b/SynthBase/SynthBaseLayer.js @@ -400,7 +400,7 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot let layers = path.connectionsGroup.getLayers(); for (let layer of layers) { layer.getLatLngs()[1] = airportPos; - layer.redraw(); + L.redrawLayer(layer); layer.updateWidgets(layer.pathLength + this.getLineLengthMeters(layer)); } } @@ -469,7 +469,7 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot color, dashArray: this.dashedLine, weight: this.lineThicknessValue, - segmentsNumber: 500, + segmentsNumber: L.GEODESIC_SEGMENTS, } }, diff --git a/SynthGridLayer/onMapZoom.js b/SynthGridLayer/onMapZoom.js index b64cde5f..7e9e690a 100644 --- a/SynthGridLayer/onMapZoom.js +++ b/SynthGridLayer/onMapZoom.js @@ -30,14 +30,14 @@ L.ALS.SynthGridLayer.prototype._onMapZoom = function () { } this._shouldHideEverything = distancePx < 200; - if (this.isDisplayed && !this._doHidePolygonWidgets) - this.hideOrShowLayer(this._shouldHideEverything, this.widgetsGroup); + this.hideOrShowLayer(this._doHidePolygonWidgets || this._shouldHideEverything, this.widgetsGroup); this._onMapPan(); // Redraw polygons } -L.ALS.SynthGridLayer.prototype.hideOrShowGroups = function (hide) { - let groups = [this.polygonGroup, this.bordersGroup, this.labelsGroup, this.widgetsGroup]; +L.ALS.SynthGridLayer.prototype.hideOrShowGroups = function (hide, shouldHideWidgets) { + let groups = [this.polygonGroup, this.bordersGroup, this.labelsGroup]; + for (let group of groups) this.hideOrShowLayer(hide, group); } \ No newline at end of file diff --git a/SynthLineLayer/SynthLineLayer.js b/SynthLineLayer/SynthLineLayer.js index fabf09a6..384e1013 100644 --- a/SynthLineLayer/SynthLineLayer.js +++ b/SynthLineLayer/SynthLineLayer.js @@ -26,7 +26,8 @@ L.ALS.SynthLineLayer = L.ALS.SynthBaseLayer.extend(/** @lends L.ALS.SynthLineLay polyline: { shapeOptions: { color: "#ff0000", - weight: this.lineThicknessValue + weight: this.lineThicknessValue, + segmentsNumber: L.GEODESIC_SEGMENTS, } } }, this.drawingGroup); @@ -59,122 +60,31 @@ L.ALS.SynthLineLayer = L.ALS.SynthBaseLayer.extend(/** @lends L.ALS.SynthLineLay }, onEditEnd: function () { - // TODO: Remove this code when new leaflet-geodesic version will be released, it'll get fully working line lengthening - - // We need to extend paths to hold whole number of images + double basis from each side and draw capture points. - // To do both, we have to "draw" a line given length (Bx) along the given (drawn by the user) line. - - // Consider a spherical triangle formed by lat and lng with the line as hypotenuse. - // Let c be line to draw, a - lat difference, b - lng difference, and A, B, C - opposite angles to these sides. - - // We can find sines of angles of a triangle formed by a line to draw along where we know lengths of all sides. - // In both triangles, C will always be equal to 90 deg, thus, sin C / sin c = 1 / sin c. - // We can find c by drawing straight line down from north pole using given length in meters. - // Then we can find a and b by using law of sines. - - // There might be two possible configurations which all will be covered by using absolute values of differences: - - // b - // C ____ A B - // | / |\ - // | / | \ c - // a | / c a | \ - // |/ |___\ - // B C b A - - // The only problem left is directions, i.e. whether we should add or subtract found sides. - // Let p1, p2 be the points of given line where p1.lng1 < p2.lng2. - // To draw a line by p1 -> p2 direction, we always add to lng and subtract from lat, if lat2 > lat1. - // To draw a line by p2 -> p1 direction, we always subtract from lng and add to lat, if lat2 > lat1. - - /* For better understanding, see this code I used for testing: - - let x1 = 0, y1 = 51.5, x2 = 2, y2 = 52, - refLength = this.getLineLengthMeters([[x1, y1], [x2, y2]]), - requiredLength = refLength / 2, - refC = turfHelpers.degreesToRadians(this.getArcAngleByLength([0, 0], refLength, true)), - c = turfHelpers.degreesToRadians(this.getArcAngleByLength([0, 0], requiredLength, true)), - // When it'll be equal to pi (180 deg), the world will collapse, and no one will need this program. - // Thus, we won't cover this case. - refSinC = Math.sin(refC), - sinC = Math.sin(c), - sinA = Math.sin(turfHelpers.degreesToRadians(Math.abs(y2 - y1))) / refSinC, - sinB = Math.sin(turfHelpers.degreesToRadians(Math.abs(x2 - x1))) / refSinC, - a = turfHelpers.radiansToDegrees(Math.asin(sinA * sinC)) * (y1 < y2 ? 1 : -1), - b = turfHelpers.radiansToDegrees(Math.asin(sinB * sinC)); - - map.addLayer(L.polyline([[y1, x1], [y2, x2]])); - map.addLayer(L.polyline([[y1, x1], [y1 + a, x1 + b]], {color: "#6c00ff"})); - */ - - // Sorry for the quality of the rest of the code, it's done for optimization - this.pathsGroup.clearLayers(); this.pointsGroup.clearLayers(); let layers = this.drawingGroup.getLayers(), color = this.getWidgetById("color0").getValue(), lineOptions = { - color, thickness: this.lineThicknessValue, + color, thickness: this.lineThicknessValue, segmentsNumber: L.GEODESIC_SEGMENTS, }; for (let layer of layers) { - let points = layer.getLatLngs(); - - for (let i = 0; i < points.length - 1; i++) { - let lineP1 = L.LatLngUtil.cloneLatLng(points[i]), - lineP2 = L.LatLngUtil.cloneLatLng(points[i + 1]), - origP1 = L.LatLngUtil.cloneLatLng(lineP1), origP2 = L.LatLngUtil.cloneLatLng(lineP2), - p1, p2; - - // Swap points when needed, so we can always subtract from first point and add to the second point - if (lineP1.lng < lineP2.lng) { - p1 = lineP1; - p2 = lineP2; - } else { - p1 = lineP2; - p2 = lineP1; - } - - let latSign = p1.lat < p2.lat ? 1: -1, - length = this.getLineLengthMeters([p1, p2]), + let latLngs = layer.getLatLngs(); + for (let i = 1; i < latLngs.length; i++) { + let extendedGeodesic = new L.Geodesic([latLngs[i - 1], latLngs[i]], lineOptions), + length = extendedGeodesic.statistics.sphericalLengthMeters, numberOfImages = Math.ceil(length / this.Bx) + 4, - sinC = this.sineOfSideC((this.Bx * numberOfImages - length) / 2), - refSinC = this.sineOfSideC(length), - sinA = Math.sin(turfHelpers.degreesToRadians(Math.abs(p2.lat - p1.lat))) / refSinC, - sinB = Math.sin(turfHelpers.degreesToRadians(Math.abs(p2.lng - p1.lng))) / refSinC, - moveByLat = this.sideLength(sinA, sinC) * latSign, - moveByLng = this.sideLength(sinB, sinC); - p1.lng -= moveByLng; - p1.lat -= moveByLat; - p2.lng += moveByLng; - p2.lat += moveByLat; - console.log(moveByLat, moveByLng); - - - /*let lineParams = MathTools.getSlopeAndIntercept([[origP1.lng, origP1.lat], [origP2.lng, origP2.lat]]), - newPoints = []; - - for (let p of [p1, p2]) { - newPoints.push([ - p.lat, - (p.lat - lineParams.intercept) / lineParams.slope - ]) - }*/ - - this.pathsGroup.addLayer(L.geodesic([p1, p2], lineOptions)); - //this.pathsGroup.addLayer(L.polyline(newPoints, {color: "#8400ff"})); - //this.map.addLayer(L.polyline([origP1, origP2])); - - // Add capture points - sinC = this.sineOfSideC(this.Bx); - moveByLat = this.sideLength(sinA, sinC) * latSign; - moveByLng = this.sideLength(sinB, sinC); - let {lat, lng} = p1, currentImage = 0; - while (currentImage < numberOfImages) { - this.pointsGroup.addLayer(this.createCapturePoint([lat, lng], color)); - lat += moveByLat; - lng += moveByLng; - currentImage++; - } + extendBy = (this.Bx * numberOfImages - length) / 2 / length; + extendedGeodesic.changeLength("both", extendBy); + this.pathsGroup.addLayer(extendedGeodesic); + + // Capture points made by constructing a line with segments number equal to the number of images + let points = new L.Geodesic(extendedGeodesic.getLatLngs(), { + ...lineOptions, + segmentsNumber: numberOfImages + }).getActualLatLngs()[0]; + + for (let point of points) + this.pointsGroup.addLayer(this.createCapturePoint([point.lat, point.lng], color)); } } @@ -192,12 +102,9 @@ L.ALS.SynthLineLayer = L.ALS.SynthBaseLayer.extend(/** @lends L.ALS.SynthLineLay this.writeToHistory(); }, - sineOfSideC: function (length) { - return Math.sin(turfHelpers.degreesToRadians(this.getArcAngleByLength([0, 0], length, true))); - }, - - sideLength: function (sinAngle, sinC) { - return Math.abs(turfHelpers.radiansToDegrees(Math.asin(sinAngle * sinC))); + calculateParameters: function () { + L.ALS.SynthBaseLayer.prototype.calculateParameters.call(this); + this.onEditEnd(); }, statics: { diff --git a/main.js b/main.js index e959cc56..2b086346 100644 --- a/main.js +++ b/main.js @@ -9,6 +9,13 @@ //require("fastestsmallesttextencoderdecoder"); window.L = require("leaflet"); + +/** + * Segments number to use when displaying L.Geodesic + * @type {number} + */ +L.GEODESIC_SEGMENTS = 1000; + L.Geodesic = require("leaflet.geodesic").GeodesicLine; require("leaflet-draw"); require("./DrawGeodesic.js"); diff --git a/package-lock.json b/package-lock.json index cdf9ae7b..03cdbafa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "leaflet": "^1.7.1", "leaflet-draw": "^1.0.4", "leaflet.coordinates": "~0.1.5", - "leaflet.geodesic": "^2.5.5-0", + "leaflet.geodesic": "github:matafokka/Leaflet.Geodesic", "package-json-versionify": "^1.0.4", "persistify": "^2.0.1", "polybooljs": "^1.2.0", @@ -6046,10 +6046,10 @@ "dev": true }, "node_modules/leaflet.geodesic": { - "version": "2.5.5-0", - "resolved": "https://registry.npmjs.org/leaflet.geodesic/-/leaflet.geodesic-2.5.5-0.tgz", - "integrity": "sha512-kuftcyl12byXTtmNTYPiF+hVS4dWVNpXKXIXiaok8vT4n4nGnMFs6DWHM1Xq5HJevbanWmLJrVW4ljgNTqTpLQ==", + "version": "2.6.1", + "resolved": "git+ssh://git@github.com/matafokka/Leaflet.Geodesic.git#ff58fda7783f91c9c4b2595903b73c5359f66703", "dev": true, + "license": "GPL-3.0", "peerDependencies": { "leaflet": "^1.5.1" } @@ -17572,10 +17572,9 @@ "dev": true }, "leaflet.geodesic": { - "version": "2.5.5-0", - "resolved": "https://registry.npmjs.org/leaflet.geodesic/-/leaflet.geodesic-2.5.5-0.tgz", - "integrity": "sha512-kuftcyl12byXTtmNTYPiF+hVS4dWVNpXKXIXiaok8vT4n4nGnMFs6DWHM1Xq5HJevbanWmLJrVW4ljgNTqTpLQ==", + "version": "git+ssh://git@github.com/matafokka/Leaflet.Geodesic.git#ff58fda7783f91c9c4b2595903b73c5359f66703", "dev": true, + "from": "leaflet.geodesic@github:matafokka/Leaflet.Geodesic", "requires": {} }, "lie": { diff --git a/package.json b/package.json index fd335a9d..a08c7c69 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "leaflet": "^1.7.1", "leaflet-draw": "^1.0.4", "leaflet.coordinates": "~0.1.5", - "leaflet.geodesic": "^2.5.5-0", + "leaflet.geodesic": "github:matafokka/Leaflet.Geodesic", "package-json-versionify": "^1.0.4", "persistify": "^2.0.1", "polybooljs": "^1.2.0", From 213250786e2e0ba851fdb6bdd5fd2634f99c6ee3 Mon Sep 17 00:00:00 2001 From: matafokka Date: Tue, 15 Feb 2022 16:37:22 +0300 Subject: [PATCH 8/9] Added serialization, deserialization and export to GeoJSON to the line layer --- SynthBase/SynthBaseLayer.js | 6 +++++ SynthGridLayer/onMapZoom.js | 2 +- SynthLineLayer/SynthLineLayer.js | 42 +++++++++++++++++++++++++++++--- SynthPolygonLayer/toGeoJSON.js | 6 ++--- package-lock.json | 14 +++++------ package.json | 2 +- 6 files changed, 56 insertions(+), 16 deletions(-) diff --git a/SynthBase/SynthBaseLayer.js b/SynthBase/SynthBaseLayer.js index 3032f484..277025a2 100644 --- a/SynthBase/SynthBaseLayer.js +++ b/SynthBase/SynthBaseLayer.js @@ -113,6 +113,12 @@ L.ALS.SynthBaseLayer = L.ALS.Layer.extend(/** @lends L.ALS.SynthBaseLayer.protot this.serializationIgnoreList.push("_airportMarker", "toUpdateThickness"); + /** + * Properties to copy to GeoJSON when exporting + * @type {string[]} + */ + this.propertiesToExport = ["cameraWidth", "cameraHeight", "pixelWidth", "focalLength", "flightHeight", "overlayBetweenPaths", "overlayBetweenImages", "imageScale", "ly", "Ly", "By", "lx", "Lx", "Bx", "GSI", "IFOV", "GIFOV", "FOV", "GFOV", "selectedArea", "timeBetweenCaptures"]; + // Add airport let icon = L.divIcon({ iconSize: null, diff --git a/SynthGridLayer/onMapZoom.js b/SynthGridLayer/onMapZoom.js index 7e9e690a..6d0f0fe5 100644 --- a/SynthGridLayer/onMapZoom.js +++ b/SynthGridLayer/onMapZoom.js @@ -35,7 +35,7 @@ L.ALS.SynthGridLayer.prototype._onMapZoom = function () { this._onMapPan(); // Redraw polygons } -L.ALS.SynthGridLayer.prototype.hideOrShowGroups = function (hide, shouldHideWidgets) { +L.ALS.SynthGridLayer.prototype.hideOrShowGroups = function (hide) { let groups = [this.polygonGroup, this.bordersGroup, this.labelsGroup]; for (let group of groups) diff --git a/SynthLineLayer/SynthLineLayer.js b/SynthLineLayer/SynthLineLayer.js index 384e1013..98fc0873 100644 --- a/SynthLineLayer/SynthLineLayer.js +++ b/SynthLineLayer/SynthLineLayer.js @@ -1,7 +1,6 @@ require("./SynthLineWizard.js"); require("./SynthLineSettings.js"); -const turfHelpers = require("@turf/helpers"); -const MathTools = require("../MathTools.js"); +const geojsonMerge = require("@mapbox/geojson-merge"); // Using this since turfHelpers.featureCollection() discards previously defined properties. /** * Geodesic line layer @@ -27,7 +26,7 @@ L.ALS.SynthLineLayer = L.ALS.SynthBaseLayer.extend(/** @lends L.ALS.SynthLineLay shapeOptions: { color: "#ff0000", weight: this.lineThicknessValue, - segmentsNumber: L.GEODESIC_SEGMENTS, + segmentsNumber: Math.round(L.GEODESIC_SEGMENTS / 4), } } }, this.drawingGroup); @@ -107,8 +106,45 @@ L.ALS.SynthLineLayer = L.ALS.SynthBaseLayer.extend(/** @lends L.ALS.SynthLineLay this.onEditEnd(); }, + toGeoJSON: function () { + let pathsMeta = {}; + for (let prop of this.propertiesToExport) { + if (this[prop] !== undefined) + pathsMeta[prop] = this[prop]; + } + + return geojsonMerge.merge([ + L.ALS.SynthBaseLayer.prototype.toGeoJSON.call(this, pathsMeta), + this.pointsGroup.toGeoJSON(), + ]); + }, + + serialize: function (seenObjects) { + let layers = this.drawingGroup.getLayers(), lines = []; + + for (let layer of layers) + lines.push(layer.getLatLngs()); + + let serialized = this.getObjectToSerializeTo(seenObjects); + serialized.lines = L.ALS.Serializable.serializeAnyObject(lines, seenObjects); + return serialized; + }, + statics: { wizard: L.ALS.SynthLineWizard, settings: new L.ALS.SynthLineSettings(), + + deserialize: function (serialized, layerSystem, settings, seenObjects) { + let object = L.ALS.Layer.deserialize(serialized, layerSystem, settings, seenObjects), + lines = L.ALS.Serializable.deserialize(serialized.lines, seenObjects); + + for (let line of lines) + object.drawingGroup.addLayer(new L.Geodesic(line, object.drawControls.polyline.shapeOptions)); + + object.onEditEnd(); + + delete object.lines; + return object; + } } }); \ No newline at end of file diff --git a/SynthPolygonLayer/toGeoJSON.js b/SynthPolygonLayer/toGeoJSON.js index e15d1602..7aac2549 100644 --- a/SynthPolygonLayer/toGeoJSON.js +++ b/SynthPolygonLayer/toGeoJSON.js @@ -25,12 +25,10 @@ L.ALS.SynthPolygonLayer.prototype.toGeoJSON = function () { } // See _calculateParameters - let parallelsProps = {name: "Flight paths by parallels"}, - meridiansProps = {name: "Flight paths by meridians"}, - params = ["cameraWidth", "cameraHeight", "pixelWidth", "focalLength", "flightHeight", "overlayBetweenPaths", "overlayBetweenImages", "imageScale", "ly", "Ly", "By", "lx", "Lx", "Bx", "GSI", "IFOV", "GIFOV", "FOV", "GFOV", "selectedArea", "timeBetweenCaptures"]; + let parallelsProps = {name: "Flight paths by parallels"}, meridiansProps = {name: "Flight paths by meridians"}; for (let prop of [parallelsProps, meridiansProps]) { - for (let param of params) + for (let param of this.propertiesToExport) prop[param] = this[param]; } diff --git a/package-lock.json b/package-lock.json index 03cdbafa..29e22b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.1", - "leaflet-advanced-layer-system": "^2.1.7" + "leaflet-advanced-layer-system": "^2.1.8" }, "devDependencies": { "@babel/core": "^7.12.3", @@ -6029,9 +6029,9 @@ "dev": true }, "node_modules/leaflet-advanced-layer-system": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.7.tgz", - "integrity": "sha512-xveypJvI5GvtgKcHtD+oVARRt5DYA5DmghpUlMX+7hnLwbVEt8ZjdtVW2LpLuvt905DREC9T8FJ4sHk6B5XuCw==" + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.8.tgz", + "integrity": "sha512-YOYy3cK1gGaqtEeVE4QXaDTjUd6e4CsoZeXi+T4tJ6GuCmHe1o0ceKp16sQAlAdVPCADHPHtb0/Ep1bdqUPDBw==" }, "node_modules/leaflet-draw": { "version": "1.0.4", @@ -17555,9 +17555,9 @@ "dev": true }, "leaflet-advanced-layer-system": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.7.tgz", - "integrity": "sha512-xveypJvI5GvtgKcHtD+oVARRt5DYA5DmghpUlMX+7hnLwbVEt8ZjdtVW2LpLuvt905DREC9T8FJ4sHk6B5XuCw==" + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/leaflet-advanced-layer-system/-/leaflet-advanced-layer-system-2.1.8.tgz", + "integrity": "sha512-YOYy3cK1gGaqtEeVE4QXaDTjUd6e4CsoZeXi+T4tJ6GuCmHe1o0ceKp16sQAlAdVPCADHPHtb0/Ep1bdqUPDBw==" }, "leaflet-draw": { "version": "1.0.4", diff --git a/package.json b/package.json index a08c7c69..ed1f0d57 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,6 @@ }, "dependencies": { "@electron/remote": "^2.0.1", - "leaflet-advanced-layer-system": "^2.1.7" + "leaflet-advanced-layer-system": "^2.1.8" } } From 1cd58fc6d400ca36680928815e61d978303dc433 Mon Sep 17 00:00:00 2001 From: matafokka Date: Tue, 15 Feb 2022 16:50:46 +0300 Subject: [PATCH 9/9] Good enough for beta version The most of the stuff I wanted to do is already here and seems relatively stable for the beta release. Also, readme update removes bizarre phrasing that I've completely missed :D --- README.md | 14 ++++++-------- locales/English.js | 2 +- locales/Russian.js | 2 +- package.json | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 60a56b41..21d7d63b 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ -# SynthFlight Alpha +# SynthFlight Beta SynthFlight is a fully client-side software for planning aerial photography. Run it either on the desktop or in a [browser online](https://matafokka.github.io/SynthFlight/). -This is an alpha version, so expect bugs, crashes, errors, missing functions, API changes, etc. +This is a beta version, so bugs, huge API changes and lack of backwards compatibility are to be expected. -For now, it only can plan photography by a graticule or grid. However, planning by custom polygons or flight paths is on its way. +Most of the planned functionality is here, however, a number of small changes will be introduced. -SynthFlight also features an advanced extendable layer system for Leaflet that will be released as a separate package when it'll be ready. - -You can use layers to try and compare different parameters to choose which ones suites you best. +A stable version will be released in May or June 2022. # Setup @@ -100,5 +98,5 @@ Yes. There will be no compatibility between SynthFlight versions until first stable release. -## Will this project will ever be finished? -Yes, because it's my master's degree. +## When a stable release will be available? +May or June 2022 diff --git a/locales/English.js b/locales/English.js index 5445699e..1e085cf0 100644 --- a/locales/English.js +++ b/locales/English.js @@ -137,7 +137,7 @@ L.ALS.Locales.addLocaleProperties("English", { // About - firstParagraph: "SynthFlight is a fully client-side software for planning aerial photography. This is an alpha version, so expect bugs, crashes, errors, missing functions, API changes, etc.", + firstParagraph: "SynthFlight is a fully client-side software for planning aerial photography. This is a beta version so bugs, huge API changes and lack of backwards compatibility are to be expected.", secondParagraphPart1: "Visit project's", secondParagraphPart2: "GitHub page", diff --git a/locales/Russian.js b/locales/Russian.js index adf8b359..bc412f48 100644 --- a/locales/Russian.js +++ b/locales/Russian.js @@ -132,7 +132,7 @@ L.ALS.Locales.addLocaleProperties("Русский", { // About - firstParagraph: "SynthFlight – это полностью клиентское программное обеспечение для проектирования аэрофотосъемочных работ. Это alpha-версия, поэтому ожидаемы баги, ошибки, отсутсвие функциональности, изменения API и т.д.", + firstParagraph: "SynthFlight – это полностью клиентское программное обеспечение для проектирования аэрофотосъемочных работ. Это beta-версия, поэтому ожидаемы баги, большие изменения API, отсутствие обратной совместимости и т.д.", secondParagraphPart1: "Посетите", secondParagraphPart2: "страницу проекта на GitHub", diff --git a/package.json b/package.json index ed1f0d57..4eff90d4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "synthflight", "productName": "SynthFlight", - "version": "0.0.17-alpha", + "version": "0.1.0-beta", "description": "A fully client-side software for planning aerial photography", "main": "electronApp.js", "browser": "index.html",