UNPKG

vgridjs

Version:

Vgrid DGGS JS

1 lines 11.5 kB
{"version":3,"sources":["../dggs/qtm.ts"],"names":[],"mappings":";;;AAmOO,SAAS,aAAa,KAAsB,EAAA;AAG/C,EAAM,MAAA,IAAI,MAAM,iBAAiB,CAAA;AACrC;AAEO,SAAS,aAAA,CAAc,GAAa,EAAA,GAAA,EAAa,UAA4B,EAAA;AAGhF,EAAM,MAAA,IAAI,MAAM,iBAAiB,CAAA;AACrC;AAEO,SAAS,cAAc,KAAuB,EAAA;AAGjD,EAAM,MAAA,IAAI,MAAM,iBAAiB,CAAA;AACrC;AAEO,SAAS,UAAU,KAAuB,EAAA;AAC7C,EAAA,IAAI,CAAC,KAAA,IAAS,KAAM,CAAA,MAAA,IAAU,CAAG,EAAA;AAC7B,IAAM,MAAA,IAAI,MAAM,2DAA2D,CAAA;AAAA;AAE/E,EAAO,OAAA,KAAA,CAAM,KAAM,CAAA,CAAA,EAAG,EAAE,CAAA;AAC5B;AAEO,SAAS,WAAA,CAAY,OAAe,UAA+B,EAAA;AACtE,EAAA,IAAI,UAAe,KAAA,MAAA,IAAa,KAAM,CAAA,MAAA,IAAU,UAAY,EAAA;AACxD,IAAA,OAAO,MAAM,MAAW,KAAA,UAAA,GAAa,CAAC,KAAK,IAAI,EAAC;AAAA;AAKpD,EAAM,MAAA,IAAI,MAAM,iBAAiB,CAAA;AACrC","file":"qtm.cjs","sourcesContent":["/**\r\n * Quarternary Triangular Mesh (QTM) implementation in TypeScript\r\n * Original Python implementation by Paulo Raposo and Randall Brown\r\n * Based on Geoffrey Dutton's conception\r\n */\r\n\r\nimport { Point, Polygon } from 'geojson';\r\n\r\ninterface Vertex {\r\n lat: number;\r\n lon: number;\r\n}\r\n\r\ninterface Facet {\r\n vertices: Vertex[];\r\n orientation?: 'u' | 'd';\r\n isNorth?: boolean;\r\n}\r\n\r\nfunction findCrossedMeridiansByLatitude(vert1: Vertex, vert2: Vertex, newLat: number): [number, number] {\r\n const theta = newLat * Math.PI / 180;\r\n const theta1 = vert1.lat * Math.PI / 180;\r\n const lamb1 = vert1.lon * Math.PI / 180;\r\n const theta2 = vert2.lat * Math.PI / 180;\r\n const lamb2 = vert2.lon * Math.PI / 180;\r\n\r\n const dlamb = lamb2 - lamb1;\r\n\r\n const x = Math.sin(theta1) * Math.cos(theta2) * Math.cos(theta) * Math.sin(dlamb);\r\n const y = Math.sin(theta1) * Math.cos(theta2) * Math.cos(theta) * Math.cos(dlamb) - Math.cos(theta1) * Math.sin(theta2) * Math.cos(theta);\r\n const z = Math.cos(theta1) * Math.cos(theta2) * Math.sin(theta) * Math.sin(dlamb);\r\n\r\n if (z * z > x * x + y * y) {\r\n throw new Error(\"Great circle doesn't reach latitude.\");\r\n }\r\n\r\n const lambm = Math.atan2(-y, x);\r\n const dlambI = Math.acos(z / Math.sqrt(x * x + y * y));\r\n\r\n const lambI1 = lamb1 + lambm - dlambI;\r\n const lambI2 = lamb1 + lambm + dlambI;\r\n\r\n const lon1 = ((lambI1 * 180 / Math.PI) + 540) % 360 - 180;\r\n const lon2 = ((lambI2 * 180 / Math.PI) + 540) % 360 - 180;\r\n\r\n return [lon1, lon2];\r\n}\r\n\r\nfunction lonCheck(lon1: number, lon2: number, pointlon1: number, pointlon2: number): number {\r\n const [lesser, greater] = [pointlon1, pointlon2].sort((a, b) => a - b);\r\n return (lon1 > lesser && lon1 < greater) ? lon1 : lon2;\r\n}\r\n\r\nfunction getMidpoint(vert1: Vertex, vert2: Vertex): Vertex {\r\n return {\r\n lat: (vert1.lat + vert2.lat) / 2,\r\n lon: (vert1.lon + vert2.lon) / 2\r\n };\r\n}\r\n\r\nfunction constructGeometry(facet: Facet): Polygon {\r\n const coordinates = facet.vertices.map(v => [v.lon, v.lat]);\r\n // Close the ring if it's not already closed\r\n if (coordinates[0][0] !== coordinates[coordinates.length - 1][0] || \r\n coordinates[0][1] !== coordinates[coordinates.length - 1][1]) {\r\n coordinates.push(coordinates[0]);\r\n }\r\n return {\r\n type: 'Polygon',\r\n coordinates: [coordinates]\r\n };\r\n}\r\n\r\nfunction divideFacet(aFacet: Facet): Facet[] {\r\n const newFacets: Facet[] = [];\r\n const newVerts: Vertex[] = [];\r\n\r\n if (aFacet.orientation) {\r\n // This is a triangle facet\r\n for (let i = 0; i < 3; i++) {\r\n if (aFacet.vertices[i].lat === aFacet.vertices[i + 1].lat || \r\n aFacet.vertices[i].lon === aFacet.vertices[i + 1].lon) {\r\n newVerts.push(getMidpoint(aFacet.vertices[i], aFacet.vertices[i + 1]));\r\n } else {\r\n const newLat = (aFacet.vertices[i].lat + aFacet.vertices[i + 1].lat) / 2;\r\n const [newLon1, newLon2] = findCrossedMeridiansByLatitude(\r\n aFacet.vertices[i], \r\n aFacet.vertices[i + 1], \r\n newLat\r\n );\r\n const newLon = lonCheck(\r\n newLon1, \r\n newLon2, \r\n aFacet.vertices[i].lon, \r\n aFacet.vertices[i + 1].lon\r\n );\r\n newVerts.push({ lat: newLat, lon: newLon });\r\n }\r\n }\r\n\r\n if (aFacet.orientation === 'u') {\r\n newFacets.push(\r\n { vertices: [newVerts[0], newVerts[1], newVerts[2], newVerts[0]], orientation: 'd' },\r\n { vertices: [newVerts[2], newVerts[1], aFacet.vertices[2], newVerts[2]], orientation: 'u' },\r\n { vertices: [aFacet.vertices[0], newVerts[0], newVerts[2], aFacet.vertices[0]], orientation: 'u' },\r\n { vertices: [newVerts[0], aFacet.vertices[1], newVerts[1], newVerts[0]], orientation: 'u' }\r\n );\r\n } else {\r\n newFacets.push(\r\n { vertices: [newVerts[2], newVerts[0], newVerts[1], newVerts[2]], orientation: 'u' },\r\n { vertices: [aFacet.vertices[0], newVerts[0], newVerts[2], aFacet.vertices[0]], orientation: 'd' },\r\n { vertices: [newVerts[2], newVerts[1], aFacet.vertices[2], newVerts[2]], orientation: 'd' },\r\n { vertices: [newVerts[0], aFacet.vertices[1], newVerts[1], newVerts[0]], orientation: 'd' }\r\n );\r\n }\r\n } else {\r\n // This is a rectangle facet\r\n if (aFacet.isNorth) {\r\n for (let i = 0; i < 4; i++) {\r\n if (i !== 2) {\r\n if (aFacet.vertices[i].lat === aFacet.vertices[i + 1].lat || \r\n aFacet.vertices[i].lon === aFacet.vertices[i + 1].lon) {\r\n newVerts.push(getMidpoint(aFacet.vertices[i], aFacet.vertices[i + 1]));\r\n } else {\r\n const newLat = (aFacet.vertices[i].lat + aFacet.vertices[i + 1].lat) / 2;\r\n const [newLon1, newLon2] = findCrossedMeridiansByLatitude(\r\n aFacet.vertices[i], \r\n aFacet.vertices[i + 1], \r\n newLat\r\n );\r\n const newLon = lonCheck(\r\n newLon1, \r\n newLon2, \r\n aFacet.vertices[i].lon, \r\n aFacet.vertices[i + 1].lon\r\n );\r\n newVerts.push({ lat: newLat, lon: newLon });\r\n }\r\n }\r\n }\r\n\r\n newFacets.push(\r\n { vertices: [newVerts[0], newVerts[1], newVerts[2], newVerts[0]], orientation: 'd' },\r\n { vertices: [newVerts[2], newVerts[1], aFacet.vertices[2], aFacet.vertices[3], newVerts[2]], isNorth: true },\r\n { vertices: [aFacet.vertices[0], newVerts[0], newVerts[2], aFacet.vertices[0]], orientation: 'u' },\r\n { vertices: [newVerts[0], aFacet.vertices[1], newVerts[1], newVerts[0]], orientation: 'u' }\r\n );\r\n } else {\r\n for (let i = 0; i < 4; i++) {\r\n if (i !== 0) {\r\n if (aFacet.vertices[i].lat === aFacet.vertices[i + 1].lat || \r\n aFacet.vertices[i].lon === aFacet.vertices[i + 1].lon) {\r\n newVerts.push(getMidpoint(aFacet.vertices[i], aFacet.vertices[i + 1]));\r\n } else {\r\n const newLat = (aFacet.vertices[i].lat + aFacet.vertices[i + 1].lat) / 2;\r\n const [newLon1, newLon2] = findCrossedMeridiansByLatitude(\r\n aFacet.vertices[i], \r\n aFacet.vertices[i + 1], \r\n newLat\r\n );\r\n const newLon = lonCheck(\r\n newLon1, \r\n newLon2, \r\n aFacet.vertices[i].lon, \r\n aFacet.vertices[i + 1].lon\r\n );\r\n newVerts.push({ lat: newLat, lon: newLon });\r\n }\r\n }\r\n }\r\n\r\n newFacets.push(\r\n { vertices: [newVerts[2], newVerts[0], newVerts[1], newVerts[2]], orientation: 'u' },\r\n { vertices: [aFacet.vertices[0], aFacet.vertices[1], newVerts[0], newVerts[2], aFacet.vertices[0]], isNorth: false },\r\n { vertices: [newVerts[2], newVerts[1], aFacet.vertices[3], newVerts[2]], orientation: 'd' },\r\n { vertices: [newVerts[1], newVerts[0], aFacet.vertices[2], newVerts[1]], orientation: 'd' }\r\n );\r\n }\r\n }\r\n\r\n return newFacets;\r\n}\r\n\r\n// Base octahedral face definitions\r\nconst BASE_FACETS: Facet[] = [\r\n // North pole triangle\r\n {\r\n vertices: [\r\n { lat: 90, lon: 0 },\r\n { lat: 0, lon: 0 },\r\n { lat: 0, lon: 90 },\r\n { lat: 90, lon: 0 }\r\n ],\r\n orientation: 'u'\r\n },\r\n // South pole triangle\r\n {\r\n vertices: [\r\n { lat: -90, lon: 0 },\r\n { lat: 0, lon: 0 },\r\n { lat: 0, lon: 90 },\r\n { lat: -90, lon: 0 }\r\n ],\r\n orientation: 'd'\r\n },\r\n // East pole triangle\r\n {\r\n vertices: [\r\n { lat: 0, lon: 90 },\r\n { lat: 0, lon: 0 },\r\n { lat: 0, lon: 180 },\r\n { lat: 0, lon: 90 }\r\n ],\r\n orientation: 'u'\r\n },\r\n // West pole triangle\r\n {\r\n vertices: [\r\n { lat: 0, lon: -90 },\r\n { lat: 0, lon: 0 },\r\n { lat: 0, lon: 90 },\r\n { lat: 0, lon: -90 }\r\n ],\r\n orientation: 'd'\r\n }\r\n];\r\n\r\nexport function qtmIdToFacet(qtmId: string): Facet {\r\n // Implementation of qtm_id_to_facet\r\n // This would need to be implemented based on your specific QTM ID format\r\n throw new Error(\"Not implemented\");\r\n}\r\n\r\nexport function latlonToQtmId(lat: number, lon: number, resolution: number): string {\r\n // Implementation of latlon_to_qtm_id\r\n // This would need to be implemented based on your specific QTM ID format\r\n throw new Error(\"Not implemented\");\r\n}\r\n\r\nexport function qtmIdToLatlon(qtmId: string): Vertex {\r\n // Implementation of qtm_id_to_latlon\r\n // This would need to be implemented based on your specific QTM ID format\r\n throw new Error(\"Not implemented\");\r\n}\r\n\r\nexport function qtmParent(qtmId: string): string {\r\n if (!qtmId || qtmId.length <= 1) {\r\n throw new Error(\"Cannot get parent of an empty or single-character QTM ID.\");\r\n }\r\n return qtmId.slice(0, -1);\r\n}\r\n\r\nexport function qtmChildren(qtmId: string, resolution?: number): string[] {\r\n if (resolution !== undefined && qtmId.length >= resolution) {\r\n return qtmId.length === resolution ? [qtmId] : [];\r\n }\r\n\r\n // Implementation of qtm_children\r\n // This would need to be implemented based on your specific QTM ID format\r\n throw new Error(\"Not implemented\");\r\n} "]}