Merge pull request #406 from Pax1601/394-retrieve-ground-altitude-at-mouse-coordinates-location

394 retrieve ground altitude at mouse coordinates location
This commit is contained in:
Pax1601 2023-09-22 15:03:55 +02:00 committed by GitHub
commit c87d18fd8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 291 additions and 8 deletions

2
.gitignore vendored
View File

@ -12,3 +12,5 @@ node_modules
!client/bin
/client/public/plugins
/client/plugins/controltips/index.js
*.hgt
/scripts/python/hgt

View File

@ -6,6 +6,7 @@ var fs = require('fs');
var atcRouter = require('./routes/api/atc');
var airbasesRouter = require('./routes/api/airbases');
var elevationRouter = require('./routes/api/elevation');
var indexRouter = require('./routes/index');
var uikitRouter = require('./routes/uikit');
var usersRouter = require('./routes/users');
@ -23,6 +24,7 @@ app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/api/atc', atcRouter);
app.use('/api/airbases', airbasesRouter);
app.use('/api/elevation', elevationRouter);
app.use('/plugins', pluginsRouter)
app.use('/users', usersRouter);
app.use('/uikit', uikitRouter);

122
client/package-lock.json generated
View File

@ -2761,6 +2761,11 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
},
"asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
@ -3294,6 +3299,11 @@
"ieee754": "^1.1.4"
}
},
"buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -3936,6 +3946,11 @@
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true
},
"events-intercept": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/events-intercept/-/events-intercept-2.0.0.tgz",
"integrity": "sha512-blk1va0zol9QOrdZt0rFXo5KMkNPVSp92Eju/Qz8THwKWKRKeE0T8Br/1aW6+Edkyq9xHYgYxn2QtOnUKPUp+Q=="
},
"evp_bytestokey": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
@ -3998,12 +4013,25 @@
"basic-auth": "^2.0.1"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
"dev": true
},
"fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"requires": {
"pend": "~1.2.0"
}
},
"filelist": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
@ -4840,6 +4868,14 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
},
"node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"node-releases": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
@ -5068,6 +5104,11 @@
"sha.js": "^2.4.8"
}
},
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@ -5129,6 +5170,14 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"promise": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz",
"integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==",
"requires": {
"asap": "~2.0.6"
}
},
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -5579,6 +5628,34 @@
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"dev": true
},
"srtm-elevation": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/srtm-elevation/-/srtm-elevation-2.1.2.tgz",
"integrity": "sha512-VAYD86JwB4l4yXNJmlTVy5ryQBu0bq50rOvPqpNu4pbKwgPrc7QijjYFKKcM+AfnDvbmlaZv+qWWMGYg+mvBVg==",
"requires": {
"extend": "^3.0.2",
"lru-cache": "^6.0.0",
"node-fetch": "^2.6.1",
"promise": "^8.1.0",
"yauzl": "^2.10.0",
"yauzl-promise": "^2.1.3"
},
"dependencies": {
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
@ -5829,6 +5906,11 @@
"nopt": "~1.0.10"
}
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@ -6114,6 +6196,20 @@
}
}
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"which-typed-array": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
@ -6183,6 +6279,32 @@
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true
},
"yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"requires": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
},
"yauzl-clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/yauzl-clone/-/yauzl-clone-1.0.4.tgz",
"integrity": "sha512-igM2RRCf3k8TvZoxR2oguuw4z1xasOnA31joCqHIyLkeWrvAc2Jgay5ISQ2ZplinkoGaJ6orCz56Ey456c5ESA==",
"requires": {
"events-intercept": "^2.0.0"
}
},
"yauzl-promise": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/yauzl-promise/-/yauzl-promise-2.1.3.tgz",
"integrity": "sha512-A1pf6fzh6eYkK0L4Qp7g9jzJSDrM6nN0bOn5T0IbY4Yo3w+YkWlHFkJP7mzknMXjqusHFHlKsK2N+4OLsK2MRA==",
"requires": {
"yauzl": "^2.9.1",
"yauzl-clone": "^1.0.4"
}
}
}
}

View File

@ -16,7 +16,8 @@
"express": "~4.16.1",
"express-basic-auth": "^1.2.1",
"morgan": "~1.9.1",
"save": "^2.9.0"
"save": "^2.9.0",
"srtm-elevation": "^2.1.2"
},
"devDependencies": {
"@babel/preset-env": "^7.21.4",

View File

@ -106,4 +106,14 @@
text-overflow: ellipsis;
white-space: nowrap;
color: var(--background-offwhite);
}
.elevation::after {
content: attr(data-value);
font-weight: bold;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--background-offwhite);
}

View File

@ -0,0 +1,28 @@
const express = require('express');
var fs = require('fs');
const router = express.Router();
const TileSet = require('srtm-elevation').TileSet;
const SRTMElevationDownloader = require('srtm-elevation').SRTMElevationDownloader;
let rawdata = fs.readFileSync('../olympus.json');
let config = JSON.parse(rawdata);
var tileset = null;
if (config["client"] === undefined || config["client"]["elevationProvider"] === undefined)
tileset = new TileSet('./hgt');
else
tileset = new TileSet('./hgt', {downloader: new SRTMElevationDownloader('./hgt', config["client"]["elevationProvider"])});
router.get( "/:lat/:lng", ( req, res ) => {
tileset.getElevation([req.params.lat, req.params.lng], function(err, elevation) {
if (err) {
console.log('getElevation failed: ' + err.message);
res.send("n/a");
} else {
res.send(String(elevation));
}
});
});
module.exports = router;

View File

@ -16,7 +16,6 @@ function listDirectories(source) {
router.get('/list', function (req, res) {
var directories = listDirectories(pluginsDirectory);
console.log(directories)
res.send(directories.filter(directory => fs.existsSync(path.join(pluginsDirectory, directory))));
});

View File

@ -101,7 +101,7 @@ export const mapBounds = {
// TODO "Falklands"
}
export const layers = {
export const mapLayers = {
"ArcGIS Satellite": {
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
maxZoom: 20,

View File

@ -12,7 +12,7 @@ import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
import { SVGInjector } from '@tanem/svg-injector'
import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTooltips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes, SHOW_UNIT_LABELS } from "../constants/constants";
import { mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTooltips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes, SHOW_UNIT_LABELS } from "../constants/constants";
import { TargetMarker } from "./markers/targetmarker";
import { CoalitionArea } from "./coalitionarea/coalitionarea";
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";

View File

@ -1,6 +1,6 @@
import { Icon, LatLng, Marker, Polyline } from "leaflet";
import { getApp } from "..";
import { distance, bearing, zeroAppend, mToNm, nmToFt } from "../other/utils";
import { distance, bearing, zeroAppend, mToNm, nmToFt, mToFt } from "../other/utils";
import { Unit } from "../unit/unit";
import { Panel } from "./panel";
import formatcoords from "formatcoords";
@ -11,6 +11,7 @@ export class MouseInfoPanel extends Panel {
#measureIcon: Icon;
#measureLine: Polyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false });
#measureBox: HTMLElement;
#elevationRequest: XMLHttpRequest | null = null;
constructor(ID: string) {
super( ID );
@ -53,6 +54,30 @@ export class MouseInfoPanel extends Panel {
var coordString = coords.format('XDDMMss', {decimalPlaces: 4});
this.#drawCoordinates("ref-mouse-position-latitude", "mouse-position-latitude", coordString.split(" ")[0]);
this.#drawCoordinates("ref-mouse-position-longitude", "mouse-position-longitude", coordString.split(" ")[1]);
/* Get the ground elevation from the server endpoint */
if (this.#elevationRequest == null) {
this.#elevationRequest = new XMLHttpRequest();
this.#elevationRequest.open('GET', `api/elevation/${mousePosition.lat}/${mousePosition.lng}`, true);
this.#elevationRequest.timeout = 500; // ms
this.#elevationRequest.responseType = 'json';
this.#elevationRequest.onload = () => {
var status = this.#elevationRequest?.status;
if (status === 200) {
const el = this.getElement().querySelector(`#mouse-position-elevation`) as HTMLElement;
try {
el.dataset.value = `${Math.floor(mToFt(parseFloat(this.#elevationRequest?.response)))} ft`;
} catch {
el.dataset.value = `N/A`;
}
}
this.#elevationRequest = null;
};
this.#elevationRequest.ontimeout = () => {this.#elevationRequest = null;}
this.#elevationRequest.onerror = () => {this.#elevationRequest = null;}
this.#elevationRequest.onabort = () => {this.#elevationRequest = null;}
this.#elevationRequest.send();
}
}
#onMapClick(e: any) {
@ -115,7 +140,6 @@ export class MouseInfoPanel extends Panel {
}
#onMouseMove(e: any) {
this.#update();
this.#drawMeasureLine();
}

View File

@ -25,9 +25,11 @@
<div id="coordinates-tool">
<dl class="ol-data-grid">
<dt id="ref-mouse-position-latitude" data-label="?"></dt>
<dd id="mouse-position-latitude" class="coordinates" data-dd="---" data-mm="---" data-ss="--" data-sss="--"></dd>
<dd id="mouse-position-latitude" class="coordinates"></dd>
<dt id="ref-mouse-position-longitude" data-label="?"></dt>
<dd id="mouse-position-longitude" class="coordinates" data-dd="---" data-mm="---" data-ss="--" data-sss="--"></dd>
<dd id="mouse-position-longitude" class="coordinates"></dd>
<dt id="ref-mouse-position-elevation" data-label="H"></dt>
<dd id="mouse-position-elevation" class="elevation" data-value="---"></dd>
</dl>
</div>

View File

@ -7,5 +7,12 @@
"gameMasterPassword": "password",
"blueCommanderPassword": "bluepassword",
"redCommanderPassword": "redpassword"
},
"client": {
"elevationProvider": {
"provider": "https://srtm.fasma.org/{lat}{lng}.SRTMGL3S.hgt.zip",
"username": null,
"password": null
}
}
}

View File

@ -0,0 +1,86 @@
import math
import urllib.request
import os
import multiprocessing
try:
os.mkdir("hgt")
except Exception as e:
print(e)
def download_file(latlng):
lat = latlng[0]
lng = latlng[1]
if lat < 0:
lat = f"S{abs(lat):02}"
else:
lat = f"N{lat:02}"
if lng < 0:
lng = f"W{abs(lng):03}"
else:
lng = f"E{lng:03}"
url = f"https://srtm.fasma.org/{lat}{lng}.SRTMGL3S.hgt.zip"
urllib.request.urlretrieve(url, f"hgt/{lat}{lng}.SRTMGL3S.hgt.zip")
print(f"{url} downloaded")
boundaries = [
[ # NTTR
39.7982463, -119.985425,
34.4037128, -119.7806729,
34.3483316, -112.4529351,
39.7372411, -112.1130805,
39.7982463, -119.985425
],
[ # Syria
37.3630556, 29.2686111,
31.8472222, 29.8975,
32.1358333, 42.1502778,
37.7177778, 42.3716667,
37.3630556, 29.2686111
],
[ # Caucasus
39.6170191, 27.634935,
38.8735863, 47.1423108,
47.3907982, 49.3101946,
48.3955879, 26.7753625,
39.6170191, 27.634935
],
[ # Persian Gulf
32.9355285, 46.5623682,
21.729393, 47.572675,
21.8501348, 63.9734737,
33.131584, 64.7313594,
32.9355285, 46.5623682
],
[ # Marianas
22.09, 135.0572222,
10.5777778, 135.7477778,
10.7725, 149.3918333,
22.5127778, 149.5427778,
22.09, 135.0572222
]
]
latlngs = []
if __name__ == '__main__':
pool = multiprocessing.Pool(32)
for boundary_set in boundaries:
lats = [boundary_set[i] for i in range(0, len(boundary_set), 2)]
lngs = [boundary_set[i] for i in range(1, len(boundary_set), 2)]
minLat = math.floor(min(lats))
minLng = math.floor(min(lngs))
maxLat = math.ceil(max(lats))
maxLng = math.ceil(max(lngs))
index = 1
for lat in range(minLat, maxLat + 1):
for lng in range(minLng, maxLng + 1):
latlngs.append((lat, lng))
print(len(latlngs))
#pool.map(download_file, latlngs)