UNPKG

@sk-global/hazard-risk

Version:

A TypeScript library for analyzing hazard risks and calculating risk assessments

1,661 lines (1,612 loc) 65.3 kB
var axios = require('axios'); var pngjs = require('pngjs'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios); // index.ts var earthRadius = 63710088e-1; var factors = { centimeters: earthRadius * 100, centimetres: earthRadius * 100, degrees: 360 / (2 * Math.PI), feet: earthRadius * 3.28084, inches: earthRadius * 39.37, kilometers: earthRadius / 1e3, kilometres: earthRadius / 1e3, meters: earthRadius, metres: earthRadius, miles: earthRadius / 1609.344, millimeters: earthRadius * 1e3, millimetres: earthRadius * 1e3, nauticalmiles: earthRadius / 1852, radians: 1, yards: earthRadius * 1.0936 }; function feature(geom, properties, options = {}) { const feat = { type: "Feature" }; if (options.id === 0 || options.id) { feat.id = options.id; } if (options.bbox) { feat.bbox = options.bbox; } feat.properties = properties || {}; feat.geometry = geom; return feat; } function point(coordinates, properties, options = {}) { if (!coordinates) { throw new Error("coordinates is required"); } if (!Array.isArray(coordinates)) { throw new Error("coordinates must be an Array"); } if (coordinates.length < 2) { throw new Error("coordinates must be at least 2 numbers long"); } if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) { throw new Error("coordinates must contain numbers"); } const geom = { type: "Point", coordinates }; return feature(geom, properties, options); } function featureCollection(features, options = {}) { const fc = { type: "FeatureCollection" }; if (options.id) { fc.id = options.id; } if (options.bbox) { fc.bbox = options.bbox; } fc.features = features; return fc; } function radiansToLength(radians, units = "kilometers") { const factor = factors[units]; if (!factor) { throw new Error(units + " units is invalid"); } return radians * factor; } function degreesToRadians(degrees) { const normalisedDegrees = degrees % 360; return normalisedDegrees * Math.PI / 180; } function isNumber(num) { return !isNaN(num) && num !== null && !Array.isArray(num); } // index.js function coordEach(geojson, callback, excludeWrapCoord) { if (geojson === null) return; var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1; for (var featureIndex = 0; featureIndex < stop; featureIndex++) { geometryMaybeCollection = isFeatureCollection ? geojson.features[featureIndex].geometry : isFeature ? geojson.geometry : geojson; isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false; stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1; for (var geomIndex = 0; geomIndex < stopG; geomIndex++) { var multiFeatureIndex = 0; var geometryIndex = 0; geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection; if (geometry === null) continue; coords = geometry.coordinates; var geomType = geometry.type; wrapShrink = excludeWrapCoord && (geomType === "Polygon" || geomType === "MultiPolygon") ? 1 : 0; switch (geomType) { case null: break; case "Point": if (callback( coords, coordIndex, featureIndex, multiFeatureIndex, geometryIndex ) === false) return false; coordIndex++; multiFeatureIndex++; break; case "LineString": case "MultiPoint": for (j = 0; j < coords.length; j++) { if (callback( coords[j], coordIndex, featureIndex, multiFeatureIndex, geometryIndex ) === false) return false; coordIndex++; if (geomType === "MultiPoint") multiFeatureIndex++; } if (geomType === "LineString") multiFeatureIndex++; break; case "Polygon": case "MultiLineString": for (j = 0; j < coords.length; j++) { for (k = 0; k < coords[j].length - wrapShrink; k++) { if (callback( coords[j][k], coordIndex, featureIndex, multiFeatureIndex, geometryIndex ) === false) return false; coordIndex++; } if (geomType === "MultiLineString") multiFeatureIndex++; if (geomType === "Polygon") geometryIndex++; } if (geomType === "Polygon") multiFeatureIndex++; break; case "MultiPolygon": for (j = 0; j < coords.length; j++) { geometryIndex = 0; for (k = 0; k < coords[j].length; k++) { for (l = 0; l < coords[j][k].length - wrapShrink; l++) { if (callback( coords[j][k][l], coordIndex, featureIndex, multiFeatureIndex, geometryIndex ) === false) return false; coordIndex++; } geometryIndex++; } multiFeatureIndex++; } break; case "GeometryCollection": for (j = 0; j < geometry.geometries.length; j++) if (coordEach(geometry.geometries[j], callback, excludeWrapCoord) === false) return false; break; default: throw new Error("Unknown Geometry Type"); } } } } function featureEach(geojson, callback) { if (geojson.type === "Feature") { callback(geojson, 0); } else if (geojson.type === "FeatureCollection") { for (var i = 0; i < geojson.features.length; i++) { if (callback(geojson.features[i], i) === false) break; } } } // index.ts function bbox(geojson, options = {}) { if (geojson.bbox != null && true !== options.recompute) { return geojson.bbox; } const result = [Infinity, Infinity, -Infinity, -Infinity]; coordEach(geojson, (coord) => { if (result[0] > coord[0]) { result[0] = coord[0]; } if (result[1] > coord[1]) { result[1] = coord[1]; } if (result[2] < coord[0]) { result[2] = coord[0]; } if (result[3] < coord[1]) { result[3] = coord[1]; } }); return result; } var turf_bbox_default = bbox; const epsilon = 1.1102230246251565e-16; const splitter = 134217729; const resulterrbound = (3 + 8 * epsilon) * epsilon; // fast_expansion_sum_zeroelim routine from oritinal code function sum(elen, e, flen, f, h) { let Q, Qnew, hh, bvirt; let enow = e[0]; let fnow = f[0]; let eindex = 0; let findex = 0; if ((fnow > enow) === (fnow > -enow)) { Q = enow; enow = e[++eindex]; } else { Q = fnow; fnow = f[++findex]; } let hindex = 0; if (eindex < elen && findex < flen) { if ((fnow > enow) === (fnow > -enow)) { Qnew = enow + Q; hh = Q - (Qnew - enow); enow = e[++eindex]; } else { Qnew = fnow + Q; hh = Q - (Qnew - fnow); fnow = f[++findex]; } Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } while (eindex < elen && findex < flen) { if ((fnow > enow) === (fnow > -enow)) { Qnew = Q + enow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (enow - bvirt); enow = e[++eindex]; } else { Qnew = Q + fnow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (fnow - bvirt); fnow = f[++findex]; } Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } } while (eindex < elen) { Qnew = Q + enow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (enow - bvirt); enow = e[++eindex]; Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } while (findex < flen) { Qnew = Q + fnow; bvirt = Qnew - Q; hh = Q - (Qnew - bvirt) + (fnow - bvirt); fnow = f[++findex]; Q = Qnew; if (hh !== 0) { h[hindex++] = hh; } } if (Q !== 0 || hindex === 0) { h[hindex++] = Q; } return hindex; } function estimate(elen, e) { let Q = e[0]; for (let i = 1; i < elen; i++) Q += e[i]; return Q; } function vec(n) { return new Float64Array(n); } const ccwerrboundA = (3 + 16 * epsilon) * epsilon; const ccwerrboundB = (2 + 12 * epsilon) * epsilon; const ccwerrboundC = (9 + 64 * epsilon) * epsilon * epsilon; const B = vec(4); const C1 = vec(8); const C2 = vec(12); const D = vec(16); const u = vec(4); function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) { let acxtail, acytail, bcxtail, bcytail; let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3; const acx = ax - cx; const bcx = bx - cx; const acy = ay - cy; const bcy = by - cy; s1 = acx * bcy; c = splitter * acx; ahi = c - (c - acx); alo = acx - ahi; c = splitter * bcy; bhi = c - (c - bcy); blo = bcy - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acy * bcx; c = splitter * acy; ahi = c - (c - acy); alo = acy - ahi; c = splitter * bcx; bhi = c - (c - bcx); blo = bcx - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; B[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; B[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; B[2] = _j - (u3 - bvirt) + (_i - bvirt); B[3] = u3; let det = estimate(4, B); let errbound = ccwerrboundB * detsum; if (det >= errbound || -det >= errbound) { return det; } bvirt = ax - acx; acxtail = ax - (acx + bvirt) + (bvirt - cx); bvirt = bx - bcx; bcxtail = bx - (bcx + bvirt) + (bvirt - cx); bvirt = ay - acy; acytail = ay - (acy + bvirt) + (bvirt - cy); bvirt = by - bcy; bcytail = by - (bcy + bvirt) + (bvirt - cy); if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) { return det; } errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det); det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); if (det >= errbound || -det >= errbound) return det; s1 = acxtail * bcy; c = splitter * acxtail; ahi = c - (c - acxtail); alo = acxtail - ahi; c = splitter * bcy; bhi = c - (c - bcy); blo = bcy - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acytail * bcx; c = splitter * acytail; ahi = c - (c - acytail); alo = acytail - ahi; c = splitter * bcx; bhi = c - (c - bcx); blo = bcx - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const C1len = sum(4, B, 4, u, C1); s1 = acx * bcytail; c = splitter * acx; ahi = c - (c - acx); alo = acx - ahi; c = splitter * bcytail; bhi = c - (c - bcytail); blo = bcytail - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acy * bcxtail; c = splitter * acy; ahi = c - (c - acy); alo = acy - ahi; c = splitter * bcxtail; bhi = c - (c - bcxtail); blo = bcxtail - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const C2len = sum(C1len, C1, 4, u, C2); s1 = acxtail * bcytail; c = splitter * acxtail; ahi = c - (c - acxtail); alo = acxtail - ahi; c = splitter * bcytail; bhi = c - (c - bcytail); blo = bcytail - bhi; s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); t1 = acytail * bcxtail; c = splitter * acytail; ahi = c - (c - acytail); alo = acytail - ahi; c = splitter * bcxtail; bhi = c - (c - bcxtail); blo = bcxtail - bhi; t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); _i = s0 - t0; bvirt = s0 - _i; u[0] = s0 - (_i + bvirt) + (bvirt - t0); _j = s1 + _i; bvirt = _j - s1; _0 = s1 - (_j - bvirt) + (_i - bvirt); _i = _0 - t1; bvirt = _0 - _i; u[1] = _0 - (_i + bvirt) + (bvirt - t1); u3 = _j + _i; bvirt = u3 - _j; u[2] = _j - (u3 - bvirt) + (_i - bvirt); u[3] = u3; const Dlen = sum(C2len, C2, 4, u, D); return D[Dlen - 1]; } function orient2d(ax, ay, bx, by, cx, cy) { const detleft = (ay - cy) * (bx - cx); const detright = (ax - cx) * (by - cy); const det = detleft - detright; const detsum = Math.abs(detleft + detright); if (Math.abs(det) >= ccwerrboundA * detsum) return det; return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum); } function pointInPolygon(p, polygon) { var i; var ii; var k = 0; var f; var u1; var v1; var u2; var v2; var currentP; var nextP; var x = p[0]; var y = p[1]; var numContours = polygon.length; for (i = 0; i < numContours; i++) { ii = 0; var contour = polygon[i]; var contourLen = contour.length - 1; currentP = contour[0]; if (currentP[0] !== contour[contourLen][0] && currentP[1] !== contour[contourLen][1]) { throw new Error('First and last coordinates in a ring must be the same') } u1 = currentP[0] - x; v1 = currentP[1] - y; for (ii; ii < contourLen; ii++) { nextP = contour[ii + 1]; u2 = nextP[0] - x; v2 = nextP[1] - y; if (v1 === 0 && v2 === 0) { if ((u2 <= 0 && u1 >= 0) || (u1 <= 0 && u2 >= 0)) { return 0 } } else if ((v2 >= 0 && v1 <= 0) || (v2 <= 0 && v1 >= 0)) { f = orient2d(u1, u2, v1, v2, 0, 0); if (f === 0) { return 0 } if ((f > 0 && v2 > 0 && v1 <= 0) || (f < 0 && v2 <= 0 && v1 > 0)) { k++; } } currentP = nextP; v1 = v2; u1 = u2; } } if (k % 2 === 0) { return false } return true } // index.ts function getCoord(coord) { if (!coord) { throw new Error("coord is required"); } if (!Array.isArray(coord)) { if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") { return [...coord.geometry.coordinates]; } if (coord.type === "Point") { return [...coord.coordinates]; } } if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) { return [...coord]; } throw new Error("coord must be GeoJSON Point or an Array of numbers"); } function getCoords(coords) { if (Array.isArray(coords)) { return coords; } if (coords.type === "Feature") { if (coords.geometry !== null) { return coords.geometry.coordinates; } } else { if (coords.coordinates) { return coords.coordinates; } } throw new Error( "coords must be GeoJSON Feature, Geometry Object or an Array" ); } function getGeom(geojson) { if (geojson.type === "Feature") { return geojson.geometry; } return geojson; } // index.ts function booleanPointInPolygon(point, polygon, options = {}) { if (!point) { throw new Error("point is required"); } if (!polygon) { throw new Error("polygon is required"); } const pt = getCoord(point); const geom = getGeom(polygon); const type = geom.type; const bbox = polygon.bbox; let polys = geom.coordinates; if (bbox && inBBox(pt, bbox) === false) { return false; } if (type === "Polygon") { polys = [polys]; } let result = false; for (var i = 0; i < polys.length; ++i) { const polyResult = pointInPolygon(pt, polys[i]); if (polyResult === 0) return options.ignoreBoundary ? false : true; else if (polyResult) result = true; } return result; } function inBBox(pt, bbox) { return bbox[0] <= pt[0] && bbox[1] <= pt[1] && bbox[2] >= pt[0] && bbox[3] >= pt[1]; } // Calculate bounding box [minX, minY, maxX, maxY] function getBoundingBox(polygon) { return turf_bbox_default(polygon); } // index.ts function booleanPointOnLine(pt, line, options = {}) { const ptCoords = getCoord(pt); const lineCoords = getCoords(line); for (let i = 0; i < lineCoords.length - 1; i++) { let ignoreBoundary = false; if (options.ignoreEndVertices) { if (i === 0) { ignoreBoundary = "start"; } if (i === lineCoords.length - 2) { ignoreBoundary = "end"; } if (i === 0 && i + 1 === lineCoords.length - 1) { ignoreBoundary = "both"; } } if (isPointOnLineSegment( lineCoords[i], lineCoords[i + 1], ptCoords, ignoreBoundary, typeof options.epsilon === "undefined" ? null : options.epsilon )) { return true; } } return false; } function isPointOnLineSegment(lineSegmentStart, lineSegmentEnd, pt, excludeBoundary, epsilon) { const x = pt[0]; const y = pt[1]; const x1 = lineSegmentStart[0]; const y1 = lineSegmentStart[1]; const x2 = lineSegmentEnd[0]; const y2 = lineSegmentEnd[1]; const dxc = pt[0] - x1; const dyc = pt[1] - y1; const dxl = x2 - x1; const dyl = y2 - y1; const cross = dxc * dyl - dyc * dxl; if (epsilon !== null) { if (Math.abs(cross) > epsilon) { return false; } } else if (cross !== 0) { return false; } if (Math.abs(dxl) === Math.abs(dyl) && Math.abs(dxl) === 0) { if (excludeBoundary) { return false; } if (pt[0] === lineSegmentStart[0] && pt[1] === lineSegmentStart[1]) { return true; } else { return false; } } if (!excludeBoundary) { if (Math.abs(dxl) >= Math.abs(dyl)) { return dxl > 0 ? x1 <= x && x <= x2 : x2 <= x && x <= x1; } return dyl > 0 ? y1 <= y && y <= y2 : y2 <= y && y <= y1; } else if (excludeBoundary === "start") { if (Math.abs(dxl) >= Math.abs(dyl)) { return dxl > 0 ? x1 < x && x <= x2 : x2 <= x && x < x1; } return dyl > 0 ? y1 < y && y <= y2 : y2 <= y && y < y1; } else if (excludeBoundary === "end") { if (Math.abs(dxl) >= Math.abs(dyl)) { return dxl > 0 ? x1 <= x && x < x2 : x2 < x && x <= x1; } return dyl > 0 ? y1 <= y && y < y2 : y2 < y && y <= y1; } else if (excludeBoundary === "both") { if (Math.abs(dxl) >= Math.abs(dyl)) { return dxl > 0 ? x1 < x && x < x2 : x2 < x && x < x1; } return dyl > 0 ? y1 < y && y < y2 : y2 < y && y < y1; } return false; } // index.ts function booleanWithin(feature1, feature2) { var geom1 = getGeom(feature1); var geom2 = getGeom(feature2); var type1 = geom1.type; var type2 = geom2.type; switch (type1) { case "Point": switch (type2) { case "MultiPoint": return isPointInMultiPoint(geom1, geom2); case "LineString": return booleanPointOnLine(geom1, geom2, { ignoreEndVertices: true }); case "Polygon": case "MultiPolygon": return booleanPointInPolygon(geom1, geom2, { ignoreBoundary: true }); default: throw new Error("feature2 " + type2 + " geometry not supported"); } case "MultiPoint": switch (type2) { case "MultiPoint": return isMultiPointInMultiPoint(geom1, geom2); case "LineString": return isMultiPointOnLine(geom1, geom2); case "Polygon": case "MultiPolygon": return isMultiPointInPoly(geom1, geom2); default: throw new Error("feature2 " + type2 + " geometry not supported"); } case "LineString": switch (type2) { case "LineString": return isLineOnLine(geom1, geom2); case "Polygon": case "MultiPolygon": return isLineInPoly(geom1, geom2); default: throw new Error("feature2 " + type2 + " geometry not supported"); } case "Polygon": switch (type2) { case "Polygon": case "MultiPolygon": return isPolyInPoly(geom1, geom2); default: throw new Error("feature2 " + type2 + " geometry not supported"); } default: throw new Error("feature1 " + type1 + " geometry not supported"); } } function isPointInMultiPoint(point, multiPoint) { var i; var output = false; for (i = 0; i < multiPoint.coordinates.length; i++) { if (compareCoords(multiPoint.coordinates[i], point.coordinates)) { output = true; break; } } return output; } function isMultiPointInMultiPoint(multiPoint1, multiPoint2) { for (var i = 0; i < multiPoint1.coordinates.length; i++) { var anyMatch = false; for (var i2 = 0; i2 < multiPoint2.coordinates.length; i2++) { if (compareCoords(multiPoint1.coordinates[i], multiPoint2.coordinates[i2])) { anyMatch = true; } } if (!anyMatch) { return false; } } return true; } function isMultiPointOnLine(multiPoint, lineString) { var foundInsidePoint = false; for (var i = 0; i < multiPoint.coordinates.length; i++) { if (!booleanPointOnLine(multiPoint.coordinates[i], lineString)) { return false; } if (!foundInsidePoint) { foundInsidePoint = booleanPointOnLine( multiPoint.coordinates[i], lineString, { ignoreEndVertices: true } ); } } return foundInsidePoint; } function isMultiPointInPoly(multiPoint, polygon) { var output = true; var isInside = false; for (var i = 0; i < multiPoint.coordinates.length; i++) { isInside = booleanPointInPolygon(multiPoint.coordinates[i], polygon); if (!isInside) { output = false; break; } { isInside = booleanPointInPolygon(multiPoint.coordinates[i], polygon, { ignoreBoundary: true }); } } return output && isInside; } function isLineOnLine(lineString1, lineString2) { for (var i = 0; i < lineString1.coordinates.length; i++) { if (!booleanPointOnLine(lineString1.coordinates[i], lineString2)) { return false; } } return true; } function isLineInPoly(linestring, polygon) { var polyBbox = bbox(polygon); var lineBbox = bbox(linestring); if (!doBBoxOverlap(polyBbox, lineBbox)) { return false; } var foundInsidePoint = false; for (var i = 0; i < linestring.coordinates.length; i++) { if (!booleanPointInPolygon(linestring.coordinates[i], polygon)) { return false; } if (!foundInsidePoint) { foundInsidePoint = booleanPointInPolygon( linestring.coordinates[i], polygon, { ignoreBoundary: true } ); } if (!foundInsidePoint && i < linestring.coordinates.length - 1) { var midpoint = getMidpoint( linestring.coordinates[i], linestring.coordinates[i + 1] ); foundInsidePoint = booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }); } } return foundInsidePoint; } function isPolyInPoly(geometry1, geometry2) { var poly1Bbox = bbox(geometry1); var poly2Bbox = bbox(geometry2); if (!doBBoxOverlap(poly2Bbox, poly1Bbox)) { return false; } for (var i = 0; i < geometry1.coordinates[0].length; i++) { if (!booleanPointInPolygon(geometry1.coordinates[0][i], geometry2)) { return false; } } return true; } function doBBoxOverlap(bbox1, bbox2) { if (bbox1[0] > bbox2[0]) return false; if (bbox1[2] < bbox2[2]) return false; if (bbox1[1] > bbox2[1]) return false; if (bbox1[3] < bbox2[3]) return false; return true; } function compareCoords(pair1, pair2) { return pair1[0] === pair2[0] && pair1[1] === pair2[1]; } function getMidpoint(pair1, pair2) { return [(pair1[0] + pair2[0]) / 2, (pair1[1] + pair2[1]) / 2]; } // index.ts function distance(from, to, options = {}) { var coordinates1 = getCoord(from); var coordinates2 = getCoord(to); var dLat = degreesToRadians(coordinates2[1] - coordinates1[1]); var dLon = degreesToRadians(coordinates2[0] - coordinates1[0]); var lat1 = degreesToRadians(coordinates1[1]); var lat2 = degreesToRadians(coordinates2[1]); var a = Math.pow(Math.sin(dLat / 2), 2) + Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2); return radiansToLength( 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)), options.units ); } // index.ts function pointGrid(bbox, cellSide, options = {}) { if (options.mask && !options.units) options.units = "kilometers"; var results = []; var west = bbox[0]; var south = bbox[1]; var east = bbox[2]; var north = bbox[3]; var xFraction = cellSide / distance([west, south], [east, south], options); var cellWidth = xFraction * (east - west); var yFraction = cellSide / distance([west, south], [west, north], options); var cellHeight = yFraction * (north - south); var bboxWidth = east - west; var bboxHeight = north - south; var columns = Math.floor(bboxWidth / cellWidth); var rows = Math.floor(bboxHeight / cellHeight); var deltaX = (bboxWidth - columns * cellWidth) / 2; var deltaY = (bboxHeight - rows * cellHeight) / 2; var currentX = west + deltaX; while (currentX <= east) { var currentY = south + deltaY; while (currentY <= north) { var cellPt = point([currentX, currentY], options.properties); if (options.mask) { if (booleanWithin(cellPt, options.mask)) results.push(cellPt); } else { results.push(cellPt); } currentY += cellHeight; } currentX += cellWidth; } return featureCollection(results); } var turf_point_grid_default = pointGrid; // Create point grid covering bounding box function createGrid(polygon, gridSize, // meters zoom) { if (!polygon || polygon.type !== 'Polygon' || !Array.isArray(polygon.coordinates)) { throw new Error('Invalid GeoJSON Polygon'); } const bbox = getBoundingBox(polygon); const pointGrids = turf_point_grid_default(bbox, gridSize, { units: 'meters', mask: feature(polygon) }); // Convert turf points to GridPoint[] const grid = []; for (const feature of pointGrids.features) { const [lon, lat] = feature.geometry.coordinates; const tile = latLonToTile(lat, lon, zoom); const pixel = latLonToPixel(lat, lon, zoom); grid.push({ lat, lon, tile, pixel, isWater: false // Default initialization }); } return grid; } // Convert lat/lon to XYZ tile function latLonToTile(lat, lon, zoom) { const n = Math.pow(2, zoom); const xtile = Math.floor((lon + 180) / 360 * n); const ytile = Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * n); return { z: zoom, x: xtile, y: ytile }; } // Convert lat/lon to pixel in tile function latLonToPixel(lat, lon, zoom) { const n = Math.pow(2, zoom); const x = (lon + 180) / 360 * n * 256; const y = (1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * n * 256; return { x: Math.floor(x % 256), y: Math.floor(y % 256) }; } // Default hazard config for tsunami (GSI Japan) const DEFAULT_TSUNAMI_CONFIG = { name: 'Tsunami', levels: { 0: { name: 'level0', color: '0,0,0', description: 'No risk' }, 1: { name: 'level1', color: '255,255,0', description: 'Attention' }, 2: { name: 'level2', color: '255,165,0', description: 'Warning' }, 3: { name: 'level3', color: '255,0,0', description: 'Very dangerous' } }, // Array of predefined water colors waterColors: ['#bed2ff', '#a8c8ff', '#8bb8ff', '#6aa8ff'] }; // Convert RGB to hex format function rgbToHex(r, g, b) { return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; } // Convert hex to RGB function hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); if (!result) { throw new Error(`Invalid hex color: ${hex}`); } return { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) }; } // Convert RGB string to RGB object function parseRgbString(rgbString) { const parts = rgbString.split(',').map(s => parseInt(s.trim())); if (parts.length !== 3) { throw new Error(`Invalid RGB string: ${rgbString}`); } return { r: parts[0], g: parts[1], b: parts[2] }; } // Normalize color to RGB object function normalizeColor(color) { if (color.startsWith('#')) { return hexToRgb(color); } else { return parseRgbString(color); } } // Create color mapping from levels config function createColorMapping(hazardConfig) { const colorMapping = {}; for (const [level, config] of Object.entries(hazardConfig.levels)) { // Normalize color to RGB string for comparison const rgb = normalizeColor(config.color); const rgbString = `${rgb.r},${rgb.g},${rgb.b}`; colorMapping[rgbString] = parseInt(level); } return colorMapping; } // Classify risk from RGB color with hazard config function classifyRiskFromRGB(r, g, b, hazardConfig = DEFAULT_TSUNAMI_CONFIG) { const colorKey = `${r},${g},${b}`; const colorMapping = createColorMapping(hazardConfig); // Check in color mapping if (colorMapping[colorKey] !== undefined) { return colorMapping[colorKey]; } // Default to level 0 (no risk) for undefined colors return 0; } // Check water color with predefined water color array function isWaterColor(r, g, b, hazardConfig = DEFAULT_TSUNAMI_CONFIG) { const waterColors = hazardConfig.waterColors || ['#bed2ff']; const hexColor = rgbToHex(r, g, b); // Check if color is in water color list return waterColors.includes(hexColor); } // Create hazard config for different hazard types function createHazardConfig(name, levels, waterColors) { return { name, levels, waterColors }; } // Fetch tile from URL with axios async function fetchTile(url, cache, coords) { // Create key for cache const cacheKey = coords || { z: 0, x: 0, y: 0 }; // Check cache first if (cache) { const cached = cache.get(cacheKey.z, cacheKey.x, cacheKey.y, url); if (cached) { return cached; } } // Fetch from network with axios try { const response = await axios__default["default"].get(url, { responseType: 'arraybuffer', timeout: 30000, // Increase timeout to 30 seconds for GSI Japan headers: { 'User-Agent': 'HazardRisk/1.0', Accept: 'image/png,image/*,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate' } }); const buffer = Buffer.from(response.data); // Check if buffer is valid if (buffer.length === 0) { throw new Error('Empty tile data'); } // Check content-type const contentType = response.headers['content-type']; if (contentType && !contentType.includes('image/')) { throw new Error(`Invalid content-type: ${contentType}`); } // Save to cache only when successful if (cache) { cache.set(cacheKey.z, cacheKey.x, cacheKey.y, url, buffer); } return buffer; } catch (error) { var _error$response, _error$message; // Handle 404 case specifically (tile doesn't exist) if (((_error$response = error.response) == null ? void 0 : _error$response.status) === 404) { console.warn(`Tile not found (404): ${url} - Treating as no risk`); // Return empty buffer instead of throwing error return Buffer.alloc(0); } // Handle timeout if (error.code === 'ECONNABORTED' || (_error$message = error.message) != null && _error$message.includes('timeout')) { console.warn(`Timeout fetching tile: ${url}`); // throw new Error(`TIMEOUT: ${url}`); return Buffer.alloc(0); } // throw new Error(`Failed to fetch tile ${url}: ${error.message}`); return Buffer.alloc(0); } } // Create tile provider from URL template function createTileProvider(urlTemplate, cache) { return async (z, x, y) => { const url = urlTemplate.replace('{z}', z.toString()).replace('{x}', x.toString()).replace('{y}', y.toString()); return await fetchTile(url, cache, { z, x, y }); }; } // Implementation for Node.js (using pngjs) class NodeRasterReader { constructor(tileProvider) { this.tileProvider = void 0; this.tileProvider = tileProvider; } async getPixelRGB(tile, pixel) { try { const tileBuffer = await this.tileProvider(tile.z, tile.x, tile.y); // Check if buffer is valid or empty (tile not found) if (!tileBuffer || tileBuffer.length === 0) { console.warn(`Empty tile buffer for tile ${tile.z}/${tile.x}/${tile.y} - Treating as no risk`); return { r: 0, g: 0, b: 0 }; // Return level 0 color (no risk) } // Use pngjs to read PNG return new Promise((resolve, reject) => { try { const png = pngjs.PNG.sync.read(tileBuffer); const { width, height, data } = png; // Check if pixel coordinates are valid if (pixel.x < 0 || pixel.x >= width || pixel.y < 0 || pixel.y >= height) { console.warn(`Invalid pixel coordinates: ${pixel.x}, ${pixel.y} for tile ${width}x${height}`); resolve({ r: 0, g: 0, b: 0 }); // Return level 0 color return; } // PNG data format: RGBA (4 bytes per pixel) const index = (pixel.y * width + pixel.x) * 4; resolve({ r: data[index] || 0, g: data[index + 1] || 0, b: data[index + 2] || 0 }); } catch (error) { console.warn(`pngjs error for tile ${tile.z}/${tile.x}/${tile.y}:`, error); // Fallback: return level 0 color resolve({ r: 0, g: 0, b: 0 }); } }); } catch (error) { console.warn(`Error reading pixel from tile ${tile.z}/${tile.x}/${tile.y}:`, error); // Fallback: return level 0 color (no risk) return { r: 0, g: 0, b: 0 }; } } async getPixelRiskInfo(tile, pixel, hazardConfig) { const rgb = await this.getPixelRGB(tile, pixel); const riskLevel = classifyRiskFromRGB(rgb.r, rgb.g, rgb.b, hazardConfig); // isWater: always false in this NodeRasterReader demo return { riskLevel, isWater: false }; } } // Implementation for Browser (using canvas) class BrowserRasterReader { constructor(hazardTileProvider, baseTileProvider, level0Color = '0,0,0') { this.hazardTileProvider = void 0; this.baseTileProvider = void 0; this.level0Color = void 0; this.hazardTileProvider = hazardTileProvider; this.baseTileProvider = baseTileProvider; this.level0Color = normalizeColor(level0Color); } async getPixelRGB(tile, pixel) { try { const tileImage = await this.hazardTileProvider(tile.z, tile.x, tile.y); // Create canvas to read pixel const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 256; canvas.height = 256; ctx.drawImage(tileImage, 0, 0); const imageData = ctx.getImageData(pixel.x, pixel.y, 1, 1); const data = imageData.data; return { r: data[0], g: data[1], b: data[2] }; } catch (error) { var _error$message2; // Handle error similarly to Node.js if ((_error$message2 = error.message) != null && _error$message2.includes('TILE_NOT_FOUND')) { console.warn(`Tile not found for ${tile.z}/${tile.x}/${tile.y} - Treating as no risk`); return this.level0Color; // Return level 0 color instead of black } console.warn(`Error reading pixel from tile ${tile.z}/${tile.x}/${tile.y}:`, error); return this.level0Color; // Return level 0 color instead of black } } async getPixelRiskInfo(tile, pixel, hazardConfig) { try { // Read hazard tile to get risk level const hazardRgb = await this.getPixelRGB(tile, pixel); const riskLevel = classifyRiskFromRGB(hazardRgb.r, hazardRgb.g, hazardRgb.b, hazardConfig); // Read base tile to detect water (if baseTileProvider exists) let isWater = false; if (this.baseTileProvider) { try { const baseTileImage = await this.baseTileProvider(tile.z, tile.x, tile.y); // Create canvas to read pixel from base tile const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 256; canvas.height = 256; ctx.drawImage(baseTileImage, 0, 0); const imageData = ctx.getImageData(pixel.x, pixel.y, 1, 1); const data = imageData.data; const baseRgb = { r: data[0], g: data[1], b: data[2] }; // Check water color isWater = isWaterColor(baseRgb.r, baseRgb.g, baseRgb.b, hazardConfig); } catch (error) { console.warn(`Error reading base tile for water detection:`, error); // If unable to read base tile, assume not water isWater = false; } } return { riskLevel, isWater }; } catch (error) { console.warn(`Error in getPixelRiskInfo:`, error); return { riskLevel: 0, isWater: false }; } } } // Tile provider for browser: fetch by axios, return ImageBitmap function createBrowserTileProvider(urlTemplate) { return async (z, x, y) => { const url = urlTemplate.replace('{z}', z.toString()).replace('{x}', x.toString()).replace('{y}', y.toString()); try { const res = await fetch(url); if (!res.ok) { if (res.status === 404) { console.warn(`Tile not found (404): ${url} - Treating as no risk`); throw new Error(`TILE_NOT_FOUND: ${url}`); } throw new Error(`HTTP ${res.status}: ${url}`); } const blob = await res.blob(); return await createImageBitmap(blob); } catch (error) { var _error$message3; // Handle timeout if ((_error$message3 = error.message) != null && _error$message3.includes('timeout')) { console.warn(`Timeout fetching tile: ${url}`); throw new Error(`TIMEOUT: ${url}`); } throw new Error(`Failed to fetch tile ${url}: ${error.message}`); } }; } // index.ts function clone(geojson) { if (!geojson) { throw new Error("geojson is required"); } switch (geojson.type) { case "Feature": return cloneFeature(geojson); case "FeatureCollection": return cloneFeatureCollection(geojson); case "Point": case "LineString": case "Polygon": case "MultiPoint": case "MultiLineString": case "MultiPolygon": case "GeometryCollection": return cloneGeometry(geojson); default: throw new Error("unknown GeoJSON type"); } } function cloneFeature(geojson) { const cloned = { type: "Feature" }; Object.keys(geojson).forEach((key) => { switch (key) { case "type": case "properties": case "geometry": return; default: cloned[key] = geojson[key]; } }); cloned.properties = cloneProperties(geojson.properties); if (geojson.geometry == null) { cloned.geometry = null; } else { cloned.geometry = cloneGeometry(geojson.geometry); } return cloned; } function cloneProperties(properties) { const cloned = {}; if (!properties) { return cloned; } Object.keys(properties).forEach((key) => { const value = properties[key]; if (typeof value === "object") { if (value === null) { cloned[key] = null; } else if (Array.isArray(value)) { cloned[key] = value.map((item) => { return item; }); } else { cloned[key] = cloneProperties(value); } } else { cloned[key] = value; } }); return cloned; } function cloneFeatureCollection(geojson) { const cloned = { type: "FeatureCollection" }; Object.keys(geojson).forEach((key) => { switch (key) { case "type": case "features": return; default: cloned[key] = geojson[key]; } }); cloned.features = geojson.features.map((feature) => { return cloneFeature(feature); }); return cloned; } function cloneGeometry(geometry) { const geom = { type: geometry.type }; if (geometry.bbox) { geom.bbox = geometry.bbox; } if (geometry.type === "GeometryCollection") { geom.geometries = geometry.geometries.map((g) => { return cloneGeometry(g); }); return geom; } geom.coordinates = deepSlice(geometry.coordinates); return geom; } function deepSlice(coords) { const cloned = coords; if (typeof cloned[0] !== "object") { return cloned.slice(); } return cloned.map((coord) => { return deepSlice(coord); }); } var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); function nearestPoint(targetPoint, points, options = {}) { if (!targetPoint) throw new Error("targetPoint is required"); if (!points) throw new Error("points is required"); let minDist = Infinity; let bestFeatureIndex = 0; featureEach(points, (pt, featureIndex) => { const distanceToPoint = distance(targetPoint, pt, options); if (distanceToPoint < minDist) { bestFeatureIndex = featureIndex; minDist = distanceToPoint; } }); const nearestPoint2 = clone(points.features[bestFeatureIndex]); return __spreadProps(__spreadValues({}, nearestPoint2), { properties: __spreadProps(__spreadValues({}, nearestPoint2.properties), { featureIndex: bestFeatureIndex, distanceToPoint: minDist }) }); } var turf_nearest_point_default = nearestPoint; // Constants for elevation calculation const POW2_8 = Math.pow(2, 8); const POW2_16 = Math.pow(2, 16); const POW2_23 = Math.pow(2, 23); const POW2_24 = Math.pow(2, 24); // No data color (128, 0, 0) const NO_DATA_R = 128; const NO_DATA_G = 0; const NO_DATA_B = 0; // Convert lat/lng to tile coordinates function latLngToTile(lat, lng, z) { const lng_rad = lng * Math.PI / 180; const R = 128 / Math.PI; const worldCoordX = R * (lng_rad + Math.PI); const pixelCoordX = worldCoordX * Math.pow(2, z); const tileCoordX = Math.floor(pixelCoordX / 256); const lat_rad = lat * Math.PI / 180; const worldCoordY = -R / 2 * Math.log((1 + Math.sin(lat_rad)) / (1 - Math.sin(lat_rad))) + 128; const pixelCoordY = worldCoordY * Math.pow(2, z); const tileCoordY = Math.floor(pixelCoordY / 256); return { tile: { z, x: tileCoordX, y: tileCoordY }, pixel: { x: Math.floor(pixelCoordX - tileCoordX * 256), y: Math.floor(pixelCoordY - tileCoordY * 256) } }; } // Get pixel RGB from PNG buffer (unified function) function getPixelFromPNG(png, x, y) { if (!png) return [0, 0, 0]; const { width, height, data } = png; if (x < 0 || x >= width || y < 0 || y >= height) { return [0, 0, 0]; } const idx = (y * width + x) * 4; return [data[idx] || 0, data[idx + 1] || 0, data[idx + 2] || 0]; } // Get pixel RGB from ImageBitmap (browser) function getPixelFromImageBitmap(imageBitmap, x, y) { // Create canvas to read pixel const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 256; canvas.height = 256; ctx.drawImage(imageBitmap, 0, 0); const imageData = ctx.getImageData(x, y, 1, 1); const data = imageData.data; return [data[0], data[1], data[2]]; } // Calculate elevation from RGB values function calculateElevationFromRGB(r, g, b) { // Check for no data color if (r === NO_DATA_R && g === NO_DATA_G && b === NO_DATA_B) { return null; } // Calculate elevation using the same formula as JavaScript code const d = r * POW2_16 + g * POW2_8 + b; let h = d < POW2_23 ? d : d - POW2_24; if (h === -POW2_23) { h = 0; } else { h *= 0.01; } return h; } // Preload tiles in parallel (generic function) async function preloadTiles(tileCoords, tileProviders) { const promises = []; for (const coord of tileCoords) { for (const provider of tileProviders) { promises.push(provider(coord.z, coord.x, coord.y)); } } await Promise.all(promises); } // Create URL list from DEM configs function createDEMUrlList(demConfigs) { const urlList = []; for (const demConfig of demConfigs) { const minzoom = Math.min(demConfig.minzoom, demConfig.maxzoom); const maxzoom = Math.max(demConfig.minzoom, demConfig.maxzoom); for (let z = maxzoom; z >= minzoom; z--) { urlList.push({ ...demConfig, zoom: z }); } } return urlList; } // Read PNG from buffer with error handling function readPNGFromBuffer(buffer) { try { return pngjs.PNG.sync.read(buffer); } catch (error) { console.warn('Failed to read PNG from buffer:', error); return undefined; } } // LRU Cache for tile images class TileCache { constructor(maxSize = 100 * 1024 * 1024, ttl = 5 * 60 * 1000) { this.cache = new Map(); this.maxSize = void 0; this.ttl = void 0; this.timestamps = new Map(); this.maxSize = maxSize; this.ttl = ttl; } // Create key for cache createKey(z, x, y, url) { return `${z}/${x}/${y}|${url}`; } // Get tile from cache get(z, x, y, url) { const key = this.createKey(z, x, y, url); const data = this.cache.get(key); const timestamp = this.timestamps.get(key); if (!data || !timestamp) { return null; } // Check TTL if (Date.now() - timestamp > this.ttl) { this.cache.delete(key); this.timestamps.delete(key); return null; } return data; } // Save tile to cache set(z, x, y, url, data) { const key = this.createKey(z, x, y, url); // Check cache size if (this.cache.size > 0 && this.getCacheSize() + data.length > this.maxSize) { this.evictOldest(); } this.cache.set(key, data); this.timestamps.set(key, Date.now()); } // Preload tiles into cache with deduplication async preloadTiles(tileUrls, zoom, tileCoords) { console.log(`🔄 Preloading tiles into cache...`); // Create unique set of all tile coordinates const uniqueTiles = new Set(); for (const coord of tileCoords) { for (const urlTemplate of tileUrls) { const url = urlTemplate.replace('{z}', coord.z.toString()).replace('{x}', coord.x.toString()).replace('{y}', coord.y.toString()); uniqueTiles.add(`${coord.z}/${coord.x}/${coord.y}|${url}`); } } console.log(`📊 Found ${uniqueTiles.size} unique tiles to preload`); // Preload with concurrency limit to avoid overwhelming server const concurrencyLimit = 5; const tiles = Array.from(uniqueTiles); const results = []; for (let i = 0; i < tiles.length; i += concurrencyLimit) { const batch = tiles.slice(i, i + concurrencyLimit); const batchPromises = batch.map(async tileKey => { const [coordPart, url] = tileKey.split('|'); const [z, x, y] = coordPart.split('/').map(Number); try { const buffer = await fetchTile(url, this, { z, x, y }); console.log(`✅ Preloaded: ${z}/${x}/${y}`); return { success: true, tile: tileKey }; } catch (error) { var _error$message; if ((_error$message = error.message) != null && _error$message.includes('TILE_NOT_FOUND')) { console.log(`⚠️ Not found: ${z}/${x}/${y}`); } else { console.warn(`❌ Failed: ${z}/${x}/${y} - ${error.message}`); } return {