import { applyProjectionDefaults } from './util.js'; // Helper function to process units and to_meter function processUnit(unit) { let result = { units: null, to_meter: undefined }; if (typeof unit === 'string') { result.units = unit.toLowerCase(); if (result.units === 'metre') { result.units = 'meter'; // Normalize 'metre' to 'meter' } if (result.units === 'meter') { result.to_meter = 1; // Only set to_meter if units are 'meter' } } else if (unit && unit.name) { result.units = unit.name.toLowerCase(); if (result.units === 'metre') { result.units = 'meter'; // Normalize 'metre' to 'meter' } result.to_meter = unit.conversion_factor; } return result; } function toValue(valueOrObject) { if (typeof valueOrObject === 'object') { return valueOrObject.value * valueOrObject.unit.conversion_factor; } return valueOrObject; } function calculateEllipsoid(value, result) { if (value.ellipsoid.radius) { result.a = value.ellipsoid.radius; result.rf = 0; } else { result.a = toValue(value.ellipsoid.semi_major_axis); if (value.ellipsoid.inverse_flattening !== undefined) { result.rf = value.ellipsoid.inverse_flattening; } else if (value.ellipsoid.semi_major_axis !== undefined && value.ellipsoid.semi_minor_axis !== undefined) { result.rf = result.a / (result.a - toValue(value.ellipsoid.semi_minor_axis)); } } } export function transformPROJJSON(projjson, result = {}) { if (!projjson || typeof projjson !== 'object') { return projjson; // Return primitive values as-is } if (projjson.type === 'BoundCRS') { transformPROJJSON(projjson.source_crs, result); if (projjson.transformation) { if (projjson.transformation.method && projjson.transformation.method.name === 'NTv2') { // Set nadgrids to the filename from the parameterfile result.nadgrids = projjson.transformation.parameters[0].value; } else { // Populate datum_params if no parameterfile is found result.datum_params = projjson.transformation.parameters.map((param) => param.value); } } return result; // Return early for BoundCRS } // Handle specific keys in PROJJSON Object.keys(projjson).forEach((key) => { const value = projjson[key]; if (value === null) { return; } switch (key) { case 'name': if (result.srsCode) { break; } result.name = value; result.srsCode = value; // Map `name` to `srsCode` break; case 'type': if (value === 'GeographicCRS') { result.projName = 'longlat'; } else if (value === 'ProjectedCRS' && projjson.conversion && projjson.conversion.method) { result.projName = projjson.conversion.method.name; // Retain original capitalization } break; case 'datum': case 'datum_ensemble': // Handle both datum and ensemble if (value.ellipsoid) { // Extract ellipsoid properties result.ellps = value.ellipsoid.name; calculateEllipsoid(value, result); } if (value.prime_meridian) { result.from_greenwich = value.prime_meridian.longitude * Math.PI / 180; // Convert to radians } break; case 'ellipsoid': result.ellps = value.name; calculateEllipsoid(value, result); break; case 'prime_meridian': result.long0 = (value.longitude || 0) * Math.PI / 180; // Convert to radians break; case 'coordinate_system': if (value.axis) { result.axis = value.axis .map((axis) => { const direction = axis.direction; if (direction === 'east') return 'e'; if (direction === 'north') return 'n'; if (direction === 'west') return 'w'; if (direction === 'south') return 's'; throw new Error(`Unknown axis direction: ${direction}`); }) .join('') + 'u'; // Combine into a single string (e.g., "enu") if (value.unit) { const { units, to_meter } = processUnit(value.unit); result.units = units; result.to_meter = to_meter; } else if (value.axis[0] && value.axis[0].unit) { const { units, to_meter } = processUnit(value.axis[0].unit); result.units = units; result.to_meter = to_meter; } } break; case 'id': if (value.authority && value.code) { result.title = value.authority + ':' + value.code; } break; case 'conversion': if (value.method && value.method.name) { result.projName = value.method.name; // Retain original capitalization } if (value.parameters) { value.parameters.forEach((param) => { const paramName = param.name.toLowerCase().replace(/\s+/g, '_'); const paramValue = param.value; if (param.unit && param.unit.conversion_factor) { result[paramName] = paramValue * param.unit.conversion_factor; // Convert to radians or meters } else if (param.unit === 'degree') { result[paramName] = paramValue * Math.PI / 180; // Convert to radians } else { result[paramName] = paramValue; } }); } break; case 'unit': if (value.name) { result.units = value.name.toLowerCase(); if (result.units === 'metre') { result.units = 'meter'; } } if (value.conversion_factor) { result.to_meter = value.conversion_factor; } break; case 'base_crs': transformPROJJSON(value, result); // Pass `result` directly result.datumCode = value.id ? value.id.authority + '_' + value.id.code : value.name; // Set datumCode break; default: // Ignore irrelevant or unneeded properties break; } }); // Additional calculated properties if (result.latitude_of_false_origin !== undefined) { result.lat0 = result.latitude_of_false_origin; // Already in radians } if (result.longitude_of_false_origin !== undefined) { result.long0 = result.longitude_of_false_origin; } if (result.latitude_of_standard_parallel !== undefined) { result.lat0 = result.latitude_of_standard_parallel; result.lat1 = result.latitude_of_standard_parallel; } if (result.latitude_of_1st_standard_parallel !== undefined) { result.lat1 = result.latitude_of_1st_standard_parallel; } if (result.latitude_of_2nd_standard_parallel !== undefined) { result.lat2 = result.latitude_of_2nd_standard_parallel; } if (result.latitude_of_projection_centre !== undefined) { result.lat0 = result.latitude_of_projection_centre; } if (result.longitude_of_projection_centre !== undefined) { result.longc = result.longitude_of_projection_centre; } if (result.easting_at_false_origin !== undefined) { result.x0 = result.easting_at_false_origin; } if (result.northing_at_false_origin !== undefined) { result.y0 = result.northing_at_false_origin; } if (result.latitude_of_natural_origin !== undefined) { result.lat0 = result.latitude_of_natural_origin; } if (result.longitude_of_natural_origin !== undefined) { result.long0 = result.longitude_of_natural_origin; } if (result.longitude_of_origin !== undefined) { result.long0 = result.longitude_of_origin; } if (result.false_easting !== undefined) { result.x0 = result.false_easting; } if (result.easting_at_projection_centre) { result.x0 = result.easting_at_projection_centre; } if (result.false_northing !== undefined) { result.y0 = result.false_northing; } if (result.northing_at_projection_centre) { result.y0 = result.northing_at_projection_centre; } if (result.standard_parallel_1 !== undefined) { result.lat1 = result.standard_parallel_1; } if (result.standard_parallel_2 !== undefined) { result.lat2 = result.standard_parallel_2; } if (result.scale_factor_at_natural_origin !== undefined) { result.k0 = result.scale_factor_at_natural_origin; } if (result.scale_factor_at_projection_centre !== undefined) { result.k0 = result.scale_factor_at_projection_centre; } if (result.scale_factor_on_pseudo_standard_parallel !== undefined) { result.k0 = result.scale_factor_on_pseudo_standard_parallel; } if (result.azimuth !== undefined) { result.alpha = result.azimuth; } if (result.azimuth_at_projection_centre !== undefined) { result.alpha = result.azimuth_at_projection_centre; } if (result.angle_from_rectified_to_skew_grid) { result.rectified_grid_angle = result.angle_from_rectified_to_skew_grid; } // Apply projection defaults applyProjectionDefaults(result); return result; }