/* * FX Map Control - https://github.com/ClemensFischer/FX-Map-Control * © 2017 Clemens Fischer */ package fxmapcontrol; import java.util.Locale; import javafx.geometry.BoundingBox; import javafx.geometry.Bounds; import javafx.geometry.Point2D; /** * Base class for azimuthal map projections. */ public abstract class AzimuthalProjection extends MapProjection { protected Location projectionCenter = new Location(0d, 0d); @Override public boolean isWebMercator() { return false; } @Override public boolean isNormalCylindrical() { return false; } @Override public boolean isAzimuthal() { return true; } @Override public double maxLatitude() { return 90d; } @Override public Point2D getMapScale(Location location) { return new Point2D(viewportScale, viewportScale); } @Override public Bounds boundingBoxToBounds(MapBoundingBox boundingBox) { if (boundingBox instanceof CenteredBoundingBox) { CenteredBoundingBox cbbox = (CenteredBoundingBox) boundingBox; Point2D center = locationToPoint(cbbox.getCenter()); double width = cbbox.getWidth(); double height = cbbox.getHeight(); return new BoundingBox(center.getX() - width / 2d, center.getY() - height / 2d, width, height); } return super.boundingBoxToBounds(boundingBox); } @Override public MapBoundingBox boundsToBoundingBox(Bounds bounds) { Location center = pointToLocation(new Point2D( bounds.getMinX() + bounds.getWidth() / 2d, bounds.getMinY() + bounds.getHeight() / 2d)); return new CenteredBoundingBox(center, bounds.getWidth(), bounds.getHeight()); // width and height in meters } @Override public double getViewportScale(double zoomLevel) { return super.getViewportScale(zoomLevel) / METERS_PER_DEGREE; } @Override public void setViewportTransform(Location projectionCenter, Location mapCenter, Point2D viewportCenter, double zoomLevel, double heading) { this.projectionCenter = projectionCenter; super.setViewportTransform(projectionCenter, mapCenter, viewportCenter, zoomLevel, heading); } @Override public String wmsQueryParameters(MapBoundingBox boundingBox, String version) { if (crsId == null || crsId.isEmpty()) { return null; } Bounds bounds = boundingBoxToBounds(boundingBox); String crs = version.startsWith("1.1.") ? "SRS" : "CRS"; return String.format(Locale.ROOT, "%s=%s,1,%f,%f&BBOX=%f,%f,%f,%f&WIDTH=%d&HEIGHT=%d", crs, crsId, projectionCenter.getLongitude(), projectionCenter.getLatitude(), bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY(), (int) Math.round(viewportScale * bounds.getWidth()), (int) Math.round(viewportScale * bounds.getHeight())); } /** * Calculates azimuth and distance in radians from location1 to location2. */ public static double[] getAzimuthDistance(Location location1, Location location2) { double lat1 = location1.getLatitude() * Math.PI / 180d; double lon1 = location1.getLongitude() * Math.PI / 180d; double lat2 = location2.getLatitude() * Math.PI / 180d; double lon2 = location2.getLongitude() * Math.PI / 180d; double cosLat1 = Math.cos(lat1); double sinLat1 = Math.sin(lat1); double cosLat2 = Math.cos(lat2); double sinLat2 = Math.sin(lat2); double cosLon12 = Math.cos(lon2 - lon1); double sinLon12 = Math.sin(lon2 - lon1); double cosDistance = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosLon12; double azimuth = Math.atan2(sinLon12, cosLat1 * sinLat2 / cosLat2 - sinLat1 * cosLon12); double distance = Math.acos(Math.max(Math.min(cosDistance, 1d), -1d)); return new double[]{azimuth, distance}; } /** * Calculates the Location of the point given by azimuth and distance in radians from location. */ public static Location getLocation(Location location, double azimuth, double distance) { double lat = location.getLatitude() * Math.PI / 180d; double sinDistance = Math.sin(distance); double cosDistance = Math.cos(distance); double cosAzimuth = Math.cos(azimuth); double sinAzimuth = Math.sin(azimuth); double cosLat1 = Math.cos(lat); double sinLat1 = Math.sin(lat); double sinLat2 = sinLat1 * cosDistance + cosLat1 * sinDistance * cosAzimuth; double lat2 = Math.asin(Math.max(Math.min(sinLat2, 1d), -1d)); double dLon = Math.atan2(sinDistance * sinAzimuth, cosLat1 * cosDistance - sinLat1 * sinDistance * cosAzimuth); return new Location(180d / Math.PI * lat2, location.getLongitude() + 180d / Math.PI * dLon); } }