@dataforsyningen/saul 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/modules/saul-core.js +3 -78
- package/modules/saul-elevation.js +20 -2
- package/package.json +1 -1
- package/test/saul-elevation.test.js +42 -15
package/modules/saul-core.js
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
import { getDHM } from './api.js'
|
|
6
6
|
import { getElevation } from './saul-elevation.js'
|
|
7
7
|
|
|
8
|
-
let iterations
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
9
|
* Converts x,y coordinates from an image to real world lat,lon coordinates
|
|
12
10
|
* @param {object} image_data - skraafoto-stac-api image data
|
|
@@ -184,84 +182,11 @@ function iterate(image_data, col, row, auth, limit = 1) {
|
|
|
184
182
|
*/
|
|
185
183
|
async function getWorldXYZ(options) {
|
|
186
184
|
|
|
187
|
-
iterations = 1
|
|
188
|
-
|
|
189
185
|
const world_xyz = image2world(options.image, options.xy[0], options.xy[1], 0)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
image: options.image,
|
|
193
|
-
terrain: options.terrain,
|
|
194
|
-
world_xyz: Array.from(world_xyz),
|
|
195
|
-
limit: 0.5
|
|
196
|
-
})
|
|
197
|
-
return better_world_xyz
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Adjusts elevation value if values are too far apart
|
|
202
|
-
* @param {number} z - elevation
|
|
203
|
-
* @param {number} delta_y - discrepancy value
|
|
204
|
-
* @returns {number} Adjusted elevation number
|
|
205
|
-
*/
|
|
206
|
-
function adjustZ(z, delta_y) {
|
|
207
|
-
let new_z
|
|
208
|
-
if (delta_y < 0) {
|
|
209
|
-
new_z = z + 0.1
|
|
210
|
-
} else {
|
|
211
|
-
new_z = z - 0.1
|
|
212
|
-
}
|
|
213
|
-
console.log('z', new_z)
|
|
214
|
-
return new_z
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Compares differences i x and y values
|
|
219
|
-
* @param {number} delta_x - x difference
|
|
220
|
-
* @param {number} delta_y - y difference
|
|
221
|
-
* @param {number} limit - difference may be within this limit
|
|
222
|
-
* @returns {boolean} Returns true if differences are within accepted limit
|
|
223
|
-
*/
|
|
224
|
-
function compareXY(delta_x, delta_y, limit) {
|
|
225
|
-
if (Math.abs(delta_x) > limit || Math.abs(delta_y) > limit) {
|
|
226
|
-
return false
|
|
227
|
-
} else {
|
|
228
|
-
return true
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Iterates world coordinate calculations in order to get a more precise result
|
|
234
|
-
* @param {object} options
|
|
235
|
-
* @param {array} options.xy - image coordinates
|
|
236
|
-
* @param {object} options.image - STAC API item
|
|
237
|
-
* @param {object} options.terrain - GeoTIFF object from getTerrainGeoTIFF() output
|
|
238
|
-
* @param {array} options.world_xy - Array with (currently guessed) world coordinates (indexes 0 and 1) and elevation (index 2). Coordinates are EPSG:25832 format.
|
|
239
|
-
* @param {number} options.limit - Return when two guesses are within this limit
|
|
240
|
-
* @returns {array} Array with world coordinates (indexes 0 and 1) and elevation (index 2). Coordinates are EPSG:25832 format.
|
|
241
|
-
*/
|
|
242
|
-
async function compareXYZ(options) {
|
|
243
|
-
|
|
244
|
-
iterations++
|
|
245
|
-
if (iterations > 50) {
|
|
246
|
-
console.error('too many iterations')
|
|
247
|
-
return false
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const z = await getElevation(options.xy[0], options.xy[1], options.terrain)
|
|
251
|
-
const world_xyz = image2world(options.image, options.xy[0], options.xy[1], z)
|
|
252
|
-
const delta_x = world_xyz[0] - options.world_xyz[0]
|
|
253
|
-
const delta_y = world_xyz[1] - options.world_xyz[1]
|
|
186
|
+
world_xyz[2] = await getElevation(world_xyz[0], world_xyz[1], options.terrain)
|
|
187
|
+
const better_world_xyz = image2world(options.image, options.xy[0], options.xy[1], world_xyz[2])
|
|
254
188
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const new_options = Object.assign({},options)
|
|
258
|
-
new_options.world_xyz = Array.from(world_xyz)
|
|
259
|
-
new_options.world_xyz[2] = adjustZ(world_xyz[2], delta_x, delta_y)
|
|
260
|
-
return await compareXYZ(new_options)
|
|
261
|
-
|
|
262
|
-
} else {
|
|
263
|
-
return world_xyz
|
|
264
|
-
}
|
|
189
|
+
return better_world_xyz
|
|
265
190
|
}
|
|
266
191
|
|
|
267
192
|
export {
|
|
@@ -36,6 +36,22 @@ async function consumeGeoTIFF(raw_data) {
|
|
|
36
36
|
return image
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function constrainToBbox(bbox, x, y) {
|
|
40
|
+
let new_x = x
|
|
41
|
+
let new_y = y
|
|
42
|
+
if (x < bbox[0]) {
|
|
43
|
+
new_x = bbox[0] + 1
|
|
44
|
+
} else if (bbox[2] < x) {
|
|
45
|
+
new_x = bbox[2] - 1
|
|
46
|
+
}
|
|
47
|
+
if (y < bbox[1]) {
|
|
48
|
+
new_y = bbox[1] + 1
|
|
49
|
+
} else if (bbox[3] < y) {
|
|
50
|
+
new_y = bbox[3] - 1
|
|
51
|
+
}
|
|
52
|
+
return [new_x, new_y]
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
/** Calculates nearest elevation for given coordinate using geoTIFF terrain data
|
|
40
56
|
* Heavily inspired by https://towardsdatascience.com/geotiff-coordinate-querying-with-javascript-5e6caaaf88cf
|
|
41
57
|
* @param {number} x - EPSG:25832 X coordinate
|
|
@@ -51,8 +67,10 @@ async function getElevation(x, y, geoTiff) {
|
|
|
51
67
|
const bboxWidth = bbox[2] - bbox[0]
|
|
52
68
|
const bboxHeight = bbox[3] - bbox[1]
|
|
53
69
|
|
|
54
|
-
const
|
|
55
|
-
|
|
70
|
+
const xy = constrainToBbox(bbox, x, y)
|
|
71
|
+
|
|
72
|
+
const widthPct = ( xy[0] - bbox[0] ) / bboxWidth
|
|
73
|
+
const heightPct = ( xy[1] - bbox[1] ) / bboxHeight
|
|
56
74
|
const xPx = Math.floor( width * widthPct )
|
|
57
75
|
const yPx = Math.floor( height * ( 1 - heightPct ) )
|
|
58
76
|
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import auth from '../config.js'
|
|
|
5
5
|
import assert from 'assert'
|
|
6
6
|
import { getTerrainGeoTIFF, getElevation } from '../modules/saul-elevation.js'
|
|
7
7
|
import { get } from '../modules/api.js'
|
|
8
|
-
import { getZ, getWorldXYZ } from '../modules/saul-core.js'
|
|
8
|
+
import { getZ, getWorldXYZ, world2image } from '../modules/saul-core.js'
|
|
9
9
|
|
|
10
10
|
// Vars
|
|
11
11
|
const stac_item = '2021_83_29_2_0019_00003995'
|
|
@@ -16,8 +16,8 @@ const max_deviation = 0.5
|
|
|
16
16
|
let url_stac = 'https://api.dataforsyningen.dk/skraafotoapi_test/search?limit=1&crs=http://www.opengis.net/def/crs/EPSG/0/25832&token=9b554b6c854184c3b0f377ffc7481585'
|
|
17
17
|
url_stac += `&ids=${ stac_item }`
|
|
18
18
|
|
|
19
|
-
function is_equalIsh(num1, num2) {
|
|
20
|
-
if (Math.abs(num1 - num2) >
|
|
19
|
+
function is_equalIsh(num1, num2, deviation = max_deviation) {
|
|
20
|
+
if (Math.abs(num1 - num2) > deviation) {
|
|
21
21
|
return false
|
|
22
22
|
} else {
|
|
23
23
|
return true
|
|
@@ -30,13 +30,38 @@ function getRandomCoordinate(bbox) {
|
|
|
30
30
|
return [x,y]
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function
|
|
33
|
+
function testGetElevationAnumberOfTimes(data, bbox, times) {
|
|
34
34
|
for (let i = 1; i <= times; i++) {
|
|
35
35
|
const world1 = getRandomCoordinate(bbox)
|
|
36
36
|
compareElevations(world1[0], world1[1], data)
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function getRandomImageXY(image) {
|
|
41
|
+
const shapex = image.properties['proj:shape'][0]
|
|
42
|
+
const shapey = image.properties['proj:shape'][1]
|
|
43
|
+
const x = Math.floor(shapex - Math.random() * shapex)
|
|
44
|
+
const y = Math.floor(shapey - Math.random() * shapey)
|
|
45
|
+
return [x,y]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function testGetWorldXYZAnumberOfTimes(item, terrain, times) {
|
|
49
|
+
for (let i = 1; i <= times; i++) {
|
|
50
|
+
const xy = getRandomImageXY(item)
|
|
51
|
+
getWorldXYZ({
|
|
52
|
+
xy: xy,
|
|
53
|
+
image: item,
|
|
54
|
+
terrain: terrain
|
|
55
|
+
}).then(world_xy => {
|
|
56
|
+
compareElevations(world_xy[0], world_xy[1], terrain) // lower left corner of image
|
|
57
|
+
const image_coords = world2image(item, world_xy[0], world_xy[1], world_xy[2])
|
|
58
|
+
assert(is_equalIsh(image_coords[0], xy[0], 0.75), `Image x coordinates ${image_coords[0]} / ${xy[0]} do not match`)
|
|
59
|
+
assert(is_equalIsh(image_coords[1], xy[1], 0.75), `Image y coordinates ${image_coords[1]} / ${xy[1]} do not match`)
|
|
60
|
+
console.log('getWorldXYZ => world2image OK')
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
40
65
|
function compareElevations(x,y,geotiff) {
|
|
41
66
|
getElevation(x, y, geotiff)
|
|
42
67
|
.then(elevation => {
|
|
@@ -52,8 +77,6 @@ function compareElevations(x,y,geotiff) {
|
|
|
52
77
|
get(url_stac)
|
|
53
78
|
.then((json) => {
|
|
54
79
|
|
|
55
|
-
console.log('Testing getTerrainGeoTIFF and getElevation')
|
|
56
|
-
|
|
57
80
|
const width = Math.round( json.features[0].properties['proj:shape'][0] * fidelity )
|
|
58
81
|
const height = Math.round( json.features[0].properties['proj:shape'][1] * fidelity )
|
|
59
82
|
|
|
@@ -62,15 +85,19 @@ get(url_stac)
|
|
|
62
85
|
getTerrainGeoTIFF(json.features[0], auth, fidelity)
|
|
63
86
|
.then(data => {
|
|
64
87
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
// Testing getElevation
|
|
89
|
+
try {
|
|
90
|
+
testGetElevationAnumberOfTimes(data, json.features[0].bbox, 5)
|
|
91
|
+
} catch(error) {
|
|
92
|
+
console.error(error)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Testing getWorldXYZ
|
|
96
|
+
try {
|
|
97
|
+
testGetWorldXYZAnumberOfTimes(json.features[0], data, 10)
|
|
98
|
+
} catch(error) {
|
|
99
|
+
console.error(error)
|
|
100
|
+
}
|
|
74
101
|
|
|
75
102
|
})
|
|
76
103
|
|