package com.revolsys.elevation.cloud.las; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.BiFunction; import org.apache.commons.io.output.ByteArrayOutputStream; import com.revolsys.collection.map.Maps; import com.revolsys.elevation.cloud.las.pointformat.LasPointFormat; import com.revolsys.geometry.cs.Area; import com.revolsys.geometry.cs.Authority; import com.revolsys.geometry.cs.Axis; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.cs.GeographicCoordinateSystem; import com.revolsys.geometry.cs.LinearUnit; import com.revolsys.geometry.cs.ProjectedCoordinateSystem; import com.revolsys.geometry.cs.Projection; import com.revolsys.geometry.cs.ProjectionParameterNames; import com.revolsys.geometry.cs.epsg.EpsgCoordinateSystems; import com.revolsys.io.Buffers; import com.revolsys.io.endian.EndianOutput; import com.revolsys.io.endian.EndianOutputStream; import com.revolsys.raster.io.format.tiff.TiffImage; import com.revolsys.util.Exceptions; import com.revolsys.util.Pair; public class LasProjection { private static final int LASF_PROJECTION_TIFF_GEO_ASCII_PARAMS = 34737; private static final int LASF_PROJECTION_TIFF_GEO_KEY_DIRECTORY_TAG = 34735; private static final int LASF_PROJECTION_TIFF_GEO_DOUBLE_PARAMS = 34736; private static final String LASF_PROJECTION = "LASF_Projection"; private static final int LASF_PROJECTION_WKT_COORDINATE_SYSTEM = 2112; @SuppressWarnings("unused") private static Object convertGeoTiffProjection(final LasPointCloudHeader header, final byte[] bytes) { try { final List<Double> doubleParams = new ArrayList<>(); { final LasVariableLengthRecord doubleParamsProperty = header .getLasProperty(new Pair<>(LASF_PROJECTION, LASF_PROJECTION_TIFF_GEO_DOUBLE_PARAMS)); if (doubleParamsProperty != null) { final byte[] doubleParamBytes = doubleParamsProperty.getBytes(); final ByteBuffer buffer = ByteBuffer.wrap(doubleParamBytes); buffer.order(ByteOrder.LITTLE_ENDIAN); for (int i = 0; i < doubleParamBytes.length / 8; i++) { final double value = buffer.getDouble(); doubleParams.add(value); } } } byte[] asciiParamsBytes; { final LasVariableLengthRecord asciiParamsProperty = header .getLasProperty(new Pair<>(LASF_PROJECTION, LASF_PROJECTION_TIFF_GEO_ASCII_PARAMS)); if (asciiParamsProperty == null) { asciiParamsBytes = new byte[0]; } else { asciiParamsBytes = asciiParamsProperty.getBytes(); } } final Map<Integer, Object> properties = new LinkedHashMap<>(); final ByteBuffer buffer = ByteBuffer.wrap(bytes); buffer.order(ByteOrder.LITTLE_ENDIAN); final int keyDirectoryVersion = Buffers.getLEUnsignedShort(buffer); final int keyRevision = Buffers.getLEUnsignedShort(buffer); final int minorRevision = Buffers.getLEUnsignedShort(buffer); final int numberOfKeys = Buffers.getLEUnsignedShort(buffer); for (int i = 0; i < numberOfKeys; i++) { final int keyId = Buffers.getLEUnsignedShort(buffer); final int tagLocation = Buffers.getLEUnsignedShort(buffer); final int count = Buffers.getLEUnsignedShort(buffer); final int offset = Buffers.getLEUnsignedShort(buffer); if (tagLocation == 0) { properties.put(keyId, offset); } else if (tagLocation == LASF_PROJECTION_TIFF_GEO_DOUBLE_PARAMS) { final double value = doubleParams.get(offset); properties.put(keyId, value); } else if (tagLocation == LASF_PROJECTION_TIFF_GEO_ASCII_PARAMS) { final String value = new String(asciiParamsBytes, offset, count, StandardCharsets.US_ASCII); properties.put(keyId, value); } } CoordinateSystem coordinateSystem = null; int coordinateSystemId = Maps.getInteger(properties, TiffImage.PROJECTED_COORDINATE_SYSTEM_ID, 0); if (coordinateSystemId == 0) { coordinateSystemId = Maps.getInteger(properties, TiffImage.GEOGRAPHIC_COORDINATE_SYSTEM_ID, 0); if (coordinateSystemId != 0) { coordinateSystem = EpsgCoordinateSystems.getCoordinateSystem(coordinateSystem); } } else if (coordinateSystemId <= 0 || coordinateSystemId == 32767) { final int geoSrid = Maps.getInteger(properties, TiffImage.GEOGRAPHIC_COORDINATE_SYSTEM_ID, 0); if (geoSrid != 0) { if (geoSrid > 0 && geoSrid < 32767) { final GeographicCoordinateSystem geographicCoordinateSystem = EpsgCoordinateSystems .getCoordinateSystem(geoSrid); final String name = "unknown"; final Projection projection = TiffImage.getProjection(properties); final Area area = null; final Map<String, Object> parameters = new LinkedHashMap<>(); TiffImage.addDoubleParameter(parameters, ProjectionParameterNames.STANDARD_PARALLEL_1, properties, TiffImage.STANDARD_PARALLEL_1_KEY); TiffImage.addDoubleParameter(parameters, ProjectionParameterNames.STANDARD_PARALLEL_2, properties, TiffImage.STANDARD_PARALLEL_2_KEY); TiffImage.addDoubleParameter(parameters, ProjectionParameterNames.LONGITUDE_OF_CENTER, properties, TiffImage.LONGITUDE_OF_CENTER_2_KEY); TiffImage.addDoubleParameter(parameters, ProjectionParameterNames.LATITUDE_OF_CENTER, properties, TiffImage.LATITUDE_OF_CENTER_2_KEY); TiffImage.addDoubleParameter(parameters, ProjectionParameterNames.FALSE_EASTING, properties, TiffImage.FALSE_EASTING_KEY); TiffImage.addDoubleParameter(parameters, ProjectionParameterNames.FALSE_NORTHING, properties, TiffImage.FALSE_NORTHING_KEY); final LinearUnit linearUnit = TiffImage.getLinearUnit(properties); final List<Axis> axis = null; final Authority authority = null; final ProjectedCoordinateSystem projectedCoordinateSystem = new ProjectedCoordinateSystem( coordinateSystemId, name, geographicCoordinateSystem, area, projection, parameters, linearUnit, axis, authority, false); coordinateSystem = EpsgCoordinateSystems.getCoordinateSystem(projectedCoordinateSystem); } } } else { coordinateSystem = EpsgCoordinateSystems.getCoordinateSystem(coordinateSystemId); } header.setCoordinateSystemInternal(coordinateSystem); return coordinateSystem; } catch (final IOException e) { throw Exceptions.wrap(e); } } public static void init( final Map<Pair<String, Integer>, BiFunction<LasPointCloudHeader, byte[], Object>> vlrfactory) { vlrfactory.put(new Pair<>(LASF_PROJECTION, LASF_PROJECTION_TIFF_GEO_KEY_DIRECTORY_TAG), LasProjection::convertGeoTiffProjection); } protected static void setCoordinateSystem(final LasPointCloudHeader header, final CoordinateSystem coordinateSystem) { if (coordinateSystem != null) { header.removeLasProperties(LASF_PROJECTION); final LasPointFormat pointFormat = header.getPointFormat(); if (pointFormat.getId() <= 5) { final int coordinateSystemId = coordinateSystem.getCoordinateSystemId(); int keyId; if (coordinateSystem instanceof ProjectedCoordinateSystem) { keyId = TiffImage.PROJECTED_COORDINATE_SYSTEM_ID; } else { keyId = TiffImage.GEOGRAPHIC_COORDINATE_SYSTEM_ID; } final ByteArrayOutputStream byteOut = new ByteArrayOutputStream(1024); try ( final EndianOutput out = new EndianOutputStream(byteOut)) { out.writeLEUnsignedShort(1); out.writeLEUnsignedShort(1); out.writeLEUnsignedShort(0); out.writeLEUnsignedShort(1); { out.writeLEUnsignedShort(keyId); out.writeLEUnsignedShort(0); out.writeLEUnsignedShort(1); out.writeLEUnsignedShort(coordinateSystemId); } } final byte[] bytes = byteOut.toByteArray(); final LasVariableLengthRecord property = new LasVariableLengthRecord(LASF_PROJECTION, LASF_PROJECTION_TIFF_GEO_KEY_DIRECTORY_TAG, "TIFF GeoKeyDirectoryTag", bytes, coordinateSystem); header.addProperty(property); } else { final String wkt = EpsgCoordinateSystems.toWkt(coordinateSystem); final byte[] stringBytes = wkt.getBytes(StandardCharsets.UTF_8); final byte[] bytes = new byte[stringBytes.length + 1]; System.arraycopy(stringBytes, 0, bytes, 0, stringBytes.length); final LasVariableLengthRecord property = new LasVariableLengthRecord(LASF_PROJECTION, LASF_PROJECTION_WKT_COORDINATE_SYSTEM, "WKT", bytes, coordinateSystem); header.addProperty(property); } } } }