package com.revolsys.gis.grid;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.revolsys.geometry.cs.CoordinateSystem;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.logging.Logs;
import com.revolsys.util.Property;
import com.revolsys.util.number.Doubles;
public class Nts1000000RectangularMapGrid extends AbstractRectangularMapGrid {
private static final Pattern NAME_PATTERN = Pattern
.compile("^" + NtsConstants.REGEX_1000000 + ".*");
private final GeometryFactory geometryFactory = GeometryFactory.floating(4326, 2);
private double precisionScale = 1;
private final double tileHeight;
private final double tileWidth;
public Nts1000000RectangularMapGrid() {
this(NtsConstants.WIDTH_1000000, NtsConstants.HEIGHT_1000000);
}
public Nts1000000RectangularMapGrid(final double width, final double height) {
this.tileWidth = width;
this.tileHeight = height;
setName("NTS 1:1 000 000");
}
public int getBlock(final String sheet) {
return Integer.parseInt(sheet.substring(0, sheet.length() - 4));
}
public BoundingBox getBoundingBox(final String mapTileName) {
final double lat = getLatitude(mapTileName);
final double lon = getLongitude(mapTileName);
return getGeometryFactory().newBoundingBox(lon, lat, lon - this.tileWidth,
lat + this.tileHeight);
}
@Override
public CoordinateSystem getCoordinateSystem() {
return this.geometryFactory.getCoordinateSystem();
}
@Override
public String getFormattedMapTileName(final String name) {
return name;
}
@Override
public GeometryFactory getGeometryFactory() {
return this.geometryFactory;
}
public double getLatitude(final int block) {
final int index = block % 10;
return NtsConstants.MAX_LATITUDE + index * NtsConstants.HEIGHT_1000000;
}
public double getLatitude(final String mapTileName) {
final int block = getNtsBlock(mapTileName);
return getLatitude(block);
}
public double getLongitude(final int block) {
final int index = block / 10;
return NtsConstants.MAX_LONGITUDE - index * NtsConstants.WIDTH_1000000;
}
public double getLongitude(final String mapTileName) {
final int block = getNtsBlock(mapTileName);
return getLongitude(block);
}
@Override
public String getMapTileName(final double x, final double y) {
final int lonRound = (int)Math.ceil(x);
final int lonRowIndex = (int)(lonRound - NtsConstants.MAX_LONGITUDE);
final int lonIndexCol = (int)(-lonRowIndex / NtsConstants.WIDTH_1000000);
final int colIndex = lonIndexCol * 10;
final int latRound = (int)Math.floor(y);
final int latIndexRow = (int)(latRound - NtsConstants.MAX_LATITUDE);
final int rowIndex = (int)(latIndexRow / NtsConstants.HEIGHT_1000000);
final int block = rowIndex + colIndex;
return String.valueOf(block);
}
/**
* 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 sourceLon = getLongitude(sheet);
final double sourceLat = getLatitude(sheet);
final double lon = Doubles.makePrecise(this.precisionScale, sourceLon + east * getTileWidth());
final double lat = sourceLat + north * getTileHeight();
return getMapTileName(lon, lat);
}
public int getNtsBlock(final String mapTileName) {
if (Property.hasValue(mapTileName)) {
final Matcher matcher = NAME_PATTERN.matcher(mapTileName);
if (matcher.matches()) {
final String name = matcher.group(1);
final int block = Integer.parseInt(name);
return block;
}
}
throw new IllegalArgumentException(mapTileName + " does not start with a valid NTS block");
}
@Override
public RectangularMapTile getTileByLocation(final double x, final double y) {
final String mapTileName = getMapTileName(x, y);
final String formattedMapTileName = getFormattedMapTileName(mapTileName);
final BoundingBox boundingBox = getBoundingBox(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 name = getMapTileName(lon, lat);
final String formattedMapTileName = getFormattedMapTileName(mapTileName);
return new SimpleRectangularMapTile(this, formattedMapTileName, name, boundingBox);
}
@Override
public double getTileHeight() {
return this.tileHeight;
}
@Override
public List<RectangularMapTile> getTiles(final BoundingBox boundingBox) {
final 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;
final int max = 100;
if (numX > max || numY > max) {
Logs.error(this, "Request would return too many tiles width=" + numX + " (max=" + max
+ ") height=" + numY + "(max=" + max + ").");
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 void setPrecisionScale(final double precisionScale) {
this.precisionScale = precisionScale;
}
}