package com.revolsys.gis.grid; import java.util.ArrayList; import java.util.List; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.cs.epsg.EpsgCoordinateSystems; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.util.number.Doubles; public class UtmRectangularMapGrid extends AbstractRectangularMapGrid { private static final CoordinateSystem COORDINATE_SYSTEM = EpsgCoordinateSystems .getCoordinateSystem(4326); private static final GeometryFactory GEOMETRY_FACTORY = GeometryFactory.floating3(4326); public static final UtmRectangularMapGrid INSTANCE = new UtmRectangularMapGrid(); public static final double MIN_LAT = -80; public static final double MIN_LON = -180; private final double tileHeight = 8; private final double tileWidth = 6; public BoundingBox getBoundingBox(final String mapTileName) { final double lat = getLatitude(mapTileName); final double lon = getLongitude(mapTileName); return GEOMETRY_FACTORY.newBoundingBox(lon, lat, lon - this.tileWidth, lat + this.tileHeight); } @Override public CoordinateSystem getCoordinateSystem() { return COORDINATE_SYSTEM; } @Override public String getFormattedMapTileName(final String name) { return name.toUpperCase(); } @Override public GeometryFactory getGeometryFactory() { return GEOMETRY_FACTORY; } public int getHorizontalZone(final double lat, final double lon) { final String mapTileName = getMapTileName(lon, lat); return getHorizontalZone(mapTileName); } public int getHorizontalZone(final String sheet) { return Integer.parseInt(sheet.substring(0, sheet.length() - 1)); } public double getLatitude(final String mapTileName) { final char zone = getVerticalZone(mapTileName); int row; if (zone == 'x') { return 72.0; } else if (zone >= 'p') { row = zone - 'e'; } else if (zone >= 'n') { row = zone - 'd'; } else { row = zone - 'c'; } return MIN_LAT + row * this.tileHeight; } public double getLongitude(final String sheet) { final int zone = getHorizontalZone(sheet); return MIN_LON + (zone - 1) * this.tileWidth; } @Override public String getMapTileName(final double x, final double y) { char letter; if (y >= 72) { letter = 'x'; } else { final double latFloor = Math.floor(y); final int row = (int)((latFloor - MIN_LAT) / this.tileHeight); letter = (char)('c' + row); if (letter >= 'n') { letter += 2; } else if (letter >= 'i') { letter += 1; } } final double lonFloor = Math.floor(x); final int zone = (int)((lonFloor - MIN_LON) / this.tileWidth) + 1; return zone + String.valueOf(letter); } /** * Get the sheet which is the specified number of sheets east and/or north * from the current sheet. * * @param sheet The current sheet. * @param east The number of sheets east. * @param north The number of sheets north. * @return The new map sheet. */ public String getMapTileName(final String sheet, final int east, final int north) { final double lon = Doubles.makePrecise(1.0, getLongitude(sheet) + east * getTileHeight()); final double lat = getLatitude(sheet) + north * getTileHeight(); return getMapTileName(lon, lat); } public int getNad27Srid(final double lon, final double lat) { return getNad27Srid(getMapTileName(lon, lat)); } public int getNad27Srid(final Geometry geometry) { return getNad27Srid(getMapTileName(geometry)); } public int getNad27Srid(final String sheet) { final int horizontalZone = getHorizontalZone(sheet); final int verticalZone = getVerticalZone(sheet); if (horizontalZone < 24 && verticalZone >= 'n') { return 26700 + horizontalZone; } else { throw new IllegalArgumentException("UTM Zone " + sheet + " is not in North America"); } } public int getNad83Srid(final double lon, final double lat) { return getNad83Srid(getMapTileName(lon, lat)); } public int getNad83Srid(final Geometry geometry) { return getNad83Srid(getMapTileName(geometry)); } public int getNad83Srid(final String sheet) { final int horizontalZone = getHorizontalZone(sheet); final int verticalZone = getVerticalZone(sheet); if (horizontalZone < 24 && verticalZone >= 'n') { return 26900 + horizontalZone; } else { throw new IllegalArgumentException("UTM Zone " + sheet + " is not in North America"); } } @Override public RectangularMapTile getTileByLocation(final double x, final double y) { final String mapTileName = getMapTileName(x, y); final BoundingBox boundingBox = getBoundingBox(mapTileName); final String formattedMapTileName = getFormattedMapTileName(mapTileName); return new SimpleRectangularMapTile(this, formattedMapTileName, mapTileName, boundingBox); } @Override public RectangularMapTile getTileByName(final String mapTileName) { final BoundingBox boundingBox = getBoundingBox(mapTileName); final double lon = boundingBox.getMaxX(); final double lat = boundingBox.getMinY(); final String tileName = getMapTileName(lon, lat); final String formattedMapTileName = getFormattedMapTileName(mapTileName); return new SimpleRectangularMapTile(this, formattedMapTileName, tileName, boundingBox); } @Override public double getTileHeight() { return this.tileHeight; } @Override public List<RectangularMapTile> getTiles(final BoundingBox boundingBox) { final com.revolsys.geometry.model.BoundingBox envelope = boundingBox .convert(getGeometryFactory()); final List<RectangularMapTile> tiles = new ArrayList<>(); final int minXCeil = (int)Math.ceil(envelope.getMinX() / this.tileWidth); final double minX = minXCeil * this.tileWidth; final int maxXCeil = (int)Math.ceil(envelope.getMaxX() / this.tileWidth) + 1; final int minYFloor = (int)Math.floor(envelope.getMinY() / this.tileHeight); final double minY = minYFloor * this.tileHeight; final int maxYCeil = (int)Math.ceil(envelope.getMaxY() / this.tileHeight); final int numX = maxXCeil - minXCeil; final int numY = maxYCeil - minYFloor; if (numX > 8 || numY > 8) { return tiles; } for (int y = 0; y < numY; y++) { final double lat = minY + y * this.tileHeight; for (int x = 0; x < numX; x++) { final double lon = minX + x * this.tileWidth; final RectangularMapTile tile = getTileByLocation(lon, lat); tiles.add(tile); } } return tiles; } @Override public double getTileWidth() { return this.tileWidth; } public char getVerticalZone(final String sheet) { return Character.toLowerCase(sheet.charAt(sheet.length() - 1)); } }