package com.revolsys.record.io.format.esri.rest.map; import java.awt.image.BufferedImage; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import com.revolsys.collection.map.MapEx; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.io.PathName; import com.revolsys.record.io.format.esri.rest.ArcGisResponse; import com.revolsys.record.io.format.esri.rest.ArcGisRestCatalog; import com.revolsys.record.io.format.esri.rest.ArcGisRestServiceContainer; import com.revolsys.record.io.format.esri.rest.CatalogElement; import com.revolsys.util.Exceptions; public class MapService extends ArcGisRestAbstractLayerService { public static MapService getMapService(String url) { url = url.replaceAll("/*MapServer/*(\\?.*)?", "") + "/MapServer"; final ArcGisRestCatalog catalog = ArcGisRestCatalog.newArcGisRestCatalog(url); final PathName path = PathName .newPathName(url.substring(catalog.getServiceUrl().toString().length())); final MapService service = catalog.getWebServiceResource(path, MapService.class); return service; } private TileInfo tileInfo; private String mapName; private String supportedImageFormatTypes; private boolean singleFusedMapCache; private int maxImageHeight; private int maxImageWidth; private long minScale = Long.MAX_VALUE; private long maxScale = 0; private boolean supportsDynamicLayers; protected MapService() { super("MapServer"); } public MapService(final ArcGisRestServiceContainer parent) { super(parent, "MapServer"); } public BoundingBox getBoundingBox(final int zoomLevel, final int tileX, final int tileY) { final TileInfo tileInfo = getTileInfo(); final double originX = tileInfo.getOriginX(); final double tileWidth = tileInfo.getModelWidth(zoomLevel); final double x1 = originX + tileWidth * tileX; final double x2 = x1 + tileWidth; final double originY = tileInfo.getOriginY(); final double tileHeight = tileInfo.getModelHeight(zoomLevel); final double y1 = originY - tileHeight * tileY; final double y2 = y1 - tileHeight; final GeometryFactory geometryFactory = tileInfo.getGeometryFactory(); return geometryFactory.newBoundingBox(x1, y1, x2, y2); } @Override public String getIconName() { return "folder:map"; } public String getMapName() { return this.mapName; } public int getMaxImageHeight() { return this.maxImageHeight; } public int getMaxImageWidth() { return this.maxImageWidth; } public long getMaxScale() { return this.maxScale; } public long getMinScale() { return this.minScale; } public double getResolution(final int zoomLevel) { final TileInfo tileInfo = getTileInfo(); final List<LevelOfDetail> levelOfDetails = tileInfo.getLevelOfDetails(); for (final LevelOfDetail levelOfDetail : levelOfDetails) { final int level = levelOfDetail.getLevel(); if (level == zoomLevel) { return levelOfDetail.getResolution(); } } return 0; } public String getSupportedImageFormatTypes() { return this.supportedImageFormatTypes; } public BufferedImage getTileImage(final int zoomLevel, final int tileX, final int tileY) { final String url = getTileUrl(zoomLevel, tileX, tileY); boolean retry = true; while (true) { try { final URLConnection connection = new URL(url).openConnection(); final InputStream in = connection.getInputStream(); return ImageIO.read(in); } catch (final FileNotFoundException e) { return null; } catch (final IOException e) { if (!retry) { throw Exceptions.wrap(e); } } retry = false; } } public TileInfo getTileInfo() { refreshIfNeeded(); return this.tileInfo; } public String getTileUrl(final int zoomLevel, final int tileX, final int tileY) { return getServiceUrl() + "/tile/" + zoomLevel + "/" + tileY + "/" + tileX; } public int getTileX(final int zoomLevel, final double x) { final TileInfo tileInfo = getTileInfo(); final double modelTileSize = tileInfo.getModelWidth(zoomLevel); final double originX = tileInfo.getOriginX(); final double deltaX = x - originX; final double ratio = deltaX / modelTileSize; int tileX = (int)Math.floor(ratio); if (tileX > 0 && ratio - tileX < 0.0001) { tileX--; } return tileX; } public int getTileY(final int zoomLevel, final double y) { final TileInfo tileInfo = getTileInfo(); final double modelTileSize = tileInfo.getModelHeight(zoomLevel); final double originY = tileInfo.getOriginY(); final double deltaY = originY - y; final double ratio = deltaY / modelTileSize; final int tileY = (int)Math.floor(ratio); return tileY; } public int getZoomLevel(final double metresPerPixel) { final TileInfo tileInfo = getTileInfo(); final List<LevelOfDetail> levelOfDetails = tileInfo.getLevelOfDetails(); LevelOfDetail previousLevel = levelOfDetails.get(0); for (final LevelOfDetail levelOfDetail : levelOfDetails) { final double levelMetresPerPixel = levelOfDetail.getResolution(); if (metresPerPixel > levelMetresPerPixel) { if (levelOfDetail == previousLevel) { return levelOfDetail.getLevel(); } else { final double previousLevelMetresPerPixel = previousLevel.getResolution(); final double range = levelMetresPerPixel - previousLevelMetresPerPixel; final double ratio = (metresPerPixel - previousLevelMetresPerPixel) / range; if (ratio < 0.8) { return previousLevel.getLevel(); } else { return levelOfDetail.getLevel(); } } } previousLevel = levelOfDetail; } return previousLevel.getLevel(); } @Override protected void initChildren(final MapEx properties, final List<CatalogElement> children, final Map<String, LayerDescription> rootLayersByName) { this.tileInfo = ArcGisResponse.newObject(TileInfo.class, properties, "tileInfo"); if (this.tileInfo != null) { this.tileInfo.setMapService(this); children.add(this.tileInfo); } super.initChildren(properties, children, rootLayersByName); } public boolean isSingleFusedMapCache() { return this.singleFusedMapCache; } public boolean isSupportsDynamicLayers() { return this.supportsDynamicLayers; } public void setMapName(final String mapName) { this.mapName = mapName; } public void setMaxImageHeight(final int maxImageHeight) { this.maxImageHeight = maxImageHeight; } public void setMaxImageWidth(final int maxImageWidth) { this.maxImageWidth = maxImageWidth; } public void setMaxScale(final long maxScale) { this.maxScale = maxScale; } public void setMinScale(final long minScale) { this.minScale = minScale; } public void setSupportedImageFormatTypes(final String supportedImageFormatTypes) { this.supportedImageFormatTypes = supportedImageFormatTypes; } public void setSupportsDynamicLayers(final boolean supportsDynamicLayers) { this.supportsDynamicLayers = supportsDynamicLayers; } }