source: josm/trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java@ 8674

Last change on this file since 8674 was 8674, checked in by Don-vip, 9 years ago

fix Checkstyle issues

  • Property svn:eol-style set to native
File size: 24.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Dimension;
7import java.awt.GridBagLayout;
8import java.awt.Point;
9import java.io.ByteArrayInputStream;
10import java.io.IOException;
11import java.io.InputStream;
12import java.net.MalformedURLException;
13import java.net.URL;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Comparator;
17import java.util.Map;
18import java.util.Set;
19import java.util.SortedSet;
20import java.util.TreeSet;
21import java.util.concurrent.ConcurrentHashMap;
22import java.util.regex.Matcher;
23import java.util.regex.Pattern;
24
25import javax.swing.JList;
26import javax.swing.JPanel;
27import javax.swing.ListSelectionModel;
28import javax.xml.XMLConstants;
29import javax.xml.namespace.QName;
30import javax.xml.parsers.DocumentBuilder;
31import javax.xml.parsers.DocumentBuilderFactory;
32import javax.xml.parsers.ParserConfigurationException;
33import javax.xml.xpath.XPath;
34import javax.xml.xpath.XPathConstants;
35import javax.xml.xpath.XPathExpression;
36import javax.xml.xpath.XPathExpressionException;
37import javax.xml.xpath.XPathFactory;
38
39import org.openstreetmap.gui.jmapviewer.Coordinate;
40import org.openstreetmap.gui.jmapviewer.Tile;
41import org.openstreetmap.gui.jmapviewer.TileXY;
42import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
43import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource;
44import org.openstreetmap.gui.jmapviewer.tilesources.TMSTileSource;
45import org.openstreetmap.josm.Main;
46import org.openstreetmap.josm.data.coor.EastNorth;
47import org.openstreetmap.josm.data.coor.LatLon;
48import org.openstreetmap.josm.data.projection.Projection;
49import org.openstreetmap.josm.data.projection.Projections;
50import org.openstreetmap.josm.gui.ExtendedDialog;
51import org.openstreetmap.josm.io.CachedFile;
52import org.openstreetmap.josm.tools.CheckParameterUtil;
53import org.openstreetmap.josm.tools.GBC;
54import org.openstreetmap.josm.tools.Utils;
55import org.w3c.dom.Document;
56import org.w3c.dom.Node;
57import org.w3c.dom.NodeList;
58
59/**
60 * Tile Source handling WMS providers
61 *
62 * @author Wiktor Niesiobędzki
63 * @since 8526
64 */
65public class WMTSTileSource extends TMSTileSource implements TemplatedTileSource {
66 private static final String PATTERN_HEADER = "\\{header\\(([^,]+),([^}]+)\\)\\}";
67
68 private static final String URL_GET_ENCODING_PARAMS = "SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER={layer}&STYLE={Style}&"
69 + "FORMAT={format}&tileMatrixSet={TileMatrixSet}&tileMatrix={TileMatrix}&tileRow={TileRow}&tileCol={TileCol}";
70
71 private static final String[] ALL_PATTERNS = {
72 PATTERN_HEADER,
73 };
74
75 private static class TileMatrix {
76 private String identifier;
77 private double scaleDenominator;
78 private EastNorth topLeftCorner;
79 private int tileWidth;
80 private int tileHeight;
81 private int matrixWidth = -1;
82 private int matrixHeight = -1;
83 }
84
85 private static class TileMatrixSet {
86 SortedSet<TileMatrix> tileMatrix = new TreeSet<>(new Comparator<TileMatrix>() {
87 @Override
88 public int compare(TileMatrix o1, TileMatrix o2) {
89 // reverse the order, so it will be from greatest (lowest zoom level) to lowest value (highest zoom level)
90 return -1 * Double.compare(o1.scaleDenominator, o2.scaleDenominator);
91 }
92 }); // sorted by zoom level
93 private String crs;
94 private String identifier;
95 }
96
97 private static class Layer {
98 private String format;
99 private String name;
100 private Map<String, TileMatrixSet> tileMatrixSetByCRS = new ConcurrentHashMap<>();
101 private String baseUrl;
102 private String style;
103 }
104
105 private enum TransferMode {
106 KVP("KVP"),
107 REST("RESTful");
108
109 private final String typeString;
110
111 TransferMode(String urlString) {
112 this.typeString = urlString;
113 }
114
115 private String getTypeString() {
116 return typeString;
117 }
118
119 private static TransferMode fromString(String s) {
120 for (TransferMode type : TransferMode.values()) {
121 if (type.getTypeString().equals(s)) {
122 return type;
123 }
124 }
125 return null;
126 }
127 }
128
129 private static final class SelectLayerDialog extends ExtendedDialog {
130 private final Layer[] layers;
131 private final JList<String> list;
132
133 public SelectLayerDialog(Collection<Layer> layers) {
134 super(Main.parent, tr("Select WMTS layer"), new String[]{tr("Add layers"), tr("Cancel")});
135 this.layers = layers.toArray(new Layer[]{});
136 this.list = new JList<>(getLayerNames(layers));
137 this.list.setPreferredSize(new Dimension(400, 400));
138 this.list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
139 JPanel panel = new JPanel(new GridBagLayout());
140 panel.add(this.list, GBC.eol().fill());
141 setContent(panel);
142 }
143
144 private static String[] getLayerNames(Collection<Layer> layers) {
145 Collection<String> ret = new ArrayList<>();
146 for (Layer layer: layers) {
147 ret.add(layer.name);
148 }
149 return ret.toArray(new String[]{});
150 }
151
152 public Layer getSelectedLayer() {
153 int index = list.getSelectedIndex();
154 if (index < 0) {
155 return null; //nothing selected
156 }
157 return layers[index];
158 }
159 }
160
161 private final Map<String, String> headers = new ConcurrentHashMap<>();
162 private Collection<Layer> layers;
163 private Layer currentLayer;
164 private TileMatrixSet currentTileMatrixSet;
165 private double crsScale;
166 private TransferMode transferMode;
167
168 /**
169 * Creates a tile source based on imagery info
170 * @param info imagery info
171 * @throws IOException if any I/O error occurs
172 */
173 public WMTSTileSource(ImageryInfo info) throws IOException {
174 super(info);
175 this.baseUrl = normalizeCapabilitiesUrl(handleTemplate(info.getUrl()));
176 this.layers = getCapabilities();
177 if (layers.size() > 1) {
178 final SelectLayerDialog layerSelection = new SelectLayerDialog(layers);
179 if (layerSelection.showDialog().getValue() == 1) {
180 this.currentLayer = layerSelection.getSelectedLayer();
181 // TODO: save layer information into ImageryInfo / ImageryPreferences?
182 }
183
184 if (this.currentLayer == null) {
185 // user canceled operation or did not choose any layer
186 throw new IllegalArgumentException(tr("No layer selected"));
187 }
188
189 } else if (layers.size() == 1) {
190 this.currentLayer = this.layers.iterator().next();
191 } else {
192 throw new IllegalArgumentException(tr("No layers defined by getCapabilities document: {0}", info.getUrl()));
193 }
194
195 initProjection();
196 }
197
198 private String handleTemplate(String url) {
199 Pattern pattern = Pattern.compile(PATTERN_HEADER);
200 StringBuffer output = new StringBuffer();
201 Matcher matcher = pattern.matcher(url);
202 while (matcher.find()) {
203 this.headers.put(matcher.group(1), matcher.group(2));
204 matcher.appendReplacement(output, "");
205 }
206 matcher.appendTail(output);
207 return output.toString();
208 }
209
210 private Collection<Layer> getCapabilities() throws IOException {
211 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
212 builderFactory.setValidating(false);
213 builderFactory.setNamespaceAware(false);
214 try {
215 builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
216 } catch (ParserConfigurationException e) {
217 //this should not happen
218 throw new IllegalArgumentException(e);
219 }
220 DocumentBuilder builder = null;
221 InputStream in = new CachedFile(baseUrl).
222 setHttpHeaders(headers).
223 setMaxAge(7 * CachedFile.DAYS).
224 setCachingStrategy(CachedFile.CachingStrategy.IfModifiedSince).
225 getInputStream();
226 try {
227 builder = builderFactory.newDocumentBuilder();
228 byte[] data = Utils.readBytesFromStream(in);
229 if (data == null || data.length == 0) {
230 throw new IllegalArgumentException("Could not read data from: " + baseUrl);
231 }
232 Document document = builder.parse(new ByteArrayInputStream(data));
233 Node getTileOperation = getByXpath(document,
234 "/Capabilities/OperationsMetadata/Operation[@name=\"GetTile\"]/DCP/HTTP/Get").item(0);
235 this.baseUrl = getStringByXpath(getTileOperation, "@href");
236 this.transferMode = TransferMode.fromString(getStringByXpath(getTileOperation,
237 "Constraint[@name=\"GetEncoding\"]/AllowedValues/Value"));
238 NodeList layersNodeList = getByXpath(document, "/Capabilities/Contents/Layer");
239 Map<String, TileMatrixSet> matrixSetById = parseMatrices(getByXpath(document, "/Capabilities/Contents/TileMatrixSet"));
240 return parseLayer(layersNodeList, matrixSetById);
241
242 } catch (Exception e) {
243 throw new IllegalArgumentException(e);
244 }
245 }
246
247 private static String normalizeCapabilitiesUrl(String url) throws MalformedURLException {
248 URL inUrl = new URL(url);
249 URL ret = new URL(inUrl.getProtocol(), inUrl.getHost(), inUrl.getPort(), inUrl.getFile());
250 return ret.toExternalForm();
251 }
252
253 private Collection<Layer> parseLayer(NodeList nodeList, Map<String, TileMatrixSet> matrixSetById) throws XPathExpressionException {
254 Collection<Layer> ret = new ArrayList<>();
255 for (int layerId = 0; layerId < nodeList.getLength(); layerId++) {
256 Node layerNode = nodeList.item(layerId);
257 Layer layer = new Layer();
258 layer.format = getStringByXpath(layerNode, "Format");
259 layer.name = getStringByXpath(layerNode, "Identifier");
260 layer.baseUrl = getStringByXpath(layerNode, "ResourceURL[@resourceType='tile']/@template");
261 layer.style = getStringByXpath(layerNode, "Style[@isDefault='true']/Identifier");
262 if (layer.style == null) {
263 layer.style = "";
264 }
265 NodeList tileMatrixSetLinks = getByXpath(layerNode, "TileMatrixSetLink");
266 for (int tileMatrixId = 0; tileMatrixId < tileMatrixSetLinks.getLength(); tileMatrixId++) {
267 Node tileMatrixLink = tileMatrixSetLinks.item(tileMatrixId);
268 TileMatrixSet tms = matrixSetById.get(getStringByXpath(tileMatrixLink, "TileMatrixSet"));
269 layer.tileMatrixSetByCRS.put(tms.crs, tms);
270 }
271 ret.add(layer);
272 }
273 return ret;
274
275 }
276
277 private Map<String, TileMatrixSet> parseMatrices(NodeList nodeList) throws XPathExpressionException {
278 Map<String, TileMatrixSet> ret = new ConcurrentHashMap<>();
279 for (int matrixSetId = 0; matrixSetId < nodeList.getLength(); matrixSetId++) {
280 Node matrixSetNode = nodeList.item(matrixSetId);
281 TileMatrixSet matrixSet = new TileMatrixSet();
282 matrixSet.identifier = getStringByXpath(matrixSetNode, "Identifier");
283 matrixSet.crs = crsToCode(getStringByXpath(matrixSetNode, "SupportedCRS"));
284 NodeList tileMatrixList = getByXpath(matrixSetNode, "TileMatrix");
285 Projection matrixProj = Projections.getProjectionByCode(matrixSet.crs);
286 if (matrixProj == null) {
287 // use current projection if none found. Maybe user is using custom string
288 matrixProj = Main.getProjection();
289 }
290 for (int matrixId = 0; matrixId < tileMatrixList.getLength(); matrixId++) {
291 Node tileMatrixNode = tileMatrixList.item(matrixId);
292 TileMatrix tileMatrix = new TileMatrix();
293 tileMatrix.identifier = getStringByXpath(tileMatrixNode, "Identifier");
294 tileMatrix.scaleDenominator = Double.parseDouble(getStringByXpath(tileMatrixNode, "ScaleDenominator"));
295 String[] topLeftCorner = getStringByXpath(tileMatrixNode, "TopLeftCorner").split(" ");
296
297 if (matrixProj.switchXY()) {
298 tileMatrix.topLeftCorner = new EastNorth(Double.parseDouble(topLeftCorner[1]), Double.parseDouble(topLeftCorner[0]));
299 } else {
300 tileMatrix.topLeftCorner = new EastNorth(Double.parseDouble(topLeftCorner[0]), Double.parseDouble(topLeftCorner[1]));
301 }
302 tileMatrix.tileHeight = Integer.parseInt(getStringByXpath(tileMatrixNode, "TileHeight"));
303 tileMatrix.tileWidth = Integer.parseInt(getStringByXpath(tileMatrixNode, "TileHeight"));
304 tileMatrix.matrixWidth = getOptionalIntegerByXpath(tileMatrixNode, "MatrixWidth");
305 tileMatrix.matrixHeight = getOptionalIntegerByXpath(tileMatrixNode, "MatrixHeight");
306 if (tileMatrix.tileHeight != tileMatrix.tileWidth) {
307 throw new AssertionError(tr("Only square tiles are supported. {0}x{1} returned by server for TileMatrix identifier {2}",
308 tileMatrix.tileHeight, tileMatrix.tileWidth, tileMatrix.identifier));
309 }
310
311 matrixSet.tileMatrix.add(tileMatrix);
312 }
313 ret.put(matrixSet.identifier, matrixSet);
314 }
315 return ret;
316 }
317
318 private static String crsToCode(String crsIdentifier) {
319 if (crsIdentifier.startsWith("urn:ogc:def:crs:")) {
320 return crsIdentifier.replaceFirst("urn:ogc:def:crs:([^:]*):.*:(.*)$", "$1:$2");
321 }
322 return crsIdentifier;
323 }
324
325 private static int getOptionalIntegerByXpath(Node document, String xpathQuery) throws XPathExpressionException {
326 String ret = getStringByXpath(document, xpathQuery);
327 if (ret == null || "".equals(ret)) {
328 return -1;
329 }
330 return Integer.parseInt(ret);
331 }
332
333 private static String getStringByXpath(Node document, String xpathQuery) throws XPathExpressionException {
334 return (String) getByXpath(document, xpathQuery, XPathConstants.STRING);
335 }
336
337 private static NodeList getByXpath(Node document, String xpathQuery) throws XPathExpressionException {
338 return (NodeList) getByXpath(document, xpathQuery, XPathConstants.NODESET);
339 }
340
341 private static Object getByXpath(Node document, String xpathQuery, QName returnType) throws XPathExpressionException {
342 XPath xpath = XPathFactory.newInstance().newXPath();
343 XPathExpression expr = xpath.compile(xpathQuery);
344 return expr.evaluate(document, returnType);
345 }
346
347 /**
348 * Initializes projection for this TileSource with current projection
349 */
350 protected void initProjection() {
351 initProjection(Main.getProjection());
352 }
353
354 /**
355 * Initializes projection for this TileSource with projection
356 * @param proj projection to be used by this TileSource
357 */
358 public void initProjection(Projection proj) {
359 this.currentTileMatrixSet = currentLayer.tileMatrixSetByCRS.get(proj.toCode());
360 if (this.currentTileMatrixSet == null) {
361 Main.warn("Unsupported CRS selected");
362 // take first, maybe it will work (if user sets custom projections, codes will not match)
363 this.currentTileMatrixSet = currentLayer.tileMatrixSetByCRS.values().iterator().next();
364 }
365 this.crsScale = getTileSize() * 0.28e-03 / proj.getMetersPerUnit();
366 }
367
368 @Override
369 public int getDefaultTileSize() {
370 return getTileSize();
371 }
372
373 // FIXME: remove in September 2015, when ImageryPreferenceEntry.tileSize will be initialized to -1 instead to 256
374 // need to leave it as it is to keep compatiblity between tested and latest JOSM versions
375 @Override
376 public int getTileSize() {
377 TileMatrix matrix = getTileMatrix(1);
378 if (matrix == null) {
379 return 1;
380 }
381 return matrix.tileHeight;
382 }
383
384 @Override
385 public String getTileUrl(int zoom, int tilex, int tiley) {
386 String url;
387 switch (transferMode) {
388 case KVP:
389 url = baseUrl + URL_GET_ENCODING_PARAMS;
390 break;
391 case REST:
392 url = currentLayer.baseUrl;
393 break;
394 default:
395 url = "";
396 break;
397 }
398
399 TileMatrix tileMatrix = getTileMatrix(zoom);
400
401 if (tileMatrix == null) {
402 return ""; // no matrix, probably unsupported CRS selected.
403 }
404
405 return url.replaceAll("\\{layer\\}", this.currentLayer.name)
406 .replaceAll("\\{format\\}", this.currentLayer.format)
407 .replaceAll("\\{TileMatrixSet\\}", this.currentTileMatrixSet.identifier)
408 .replaceAll("\\{TileMatrix\\}", tileMatrix.identifier)
409 .replaceAll("\\{TileRow\\}", Integer.toString(tiley))
410 .replaceAll("\\{TileCol\\}", Integer.toString(tilex))
411 .replaceAll("\\{Style\\}", this.currentLayer.style);
412 }
413
414 /**
415 *
416 * @param zoom zoom level
417 * @return TileMatrix that's working on this zoom level
418 */
419 private TileMatrix getTileMatrix(int zoom) {
420 if (zoom > getMaxZoom()) {
421 return null;
422 }
423 if (zoom < 1) {
424 return null;
425 }
426 return this.currentTileMatrixSet.tileMatrix.toArray(new TileMatrix[]{})[zoom - 1];
427 }
428
429 @Override
430 public double getDistance(double lat1, double lon1, double lat2, double lon2) {
431 throw new UnsupportedOperationException("Not implemented");
432 }
433
434 @Override
435 public int lonToX(double lon, int zoom) {
436 throw new UnsupportedOperationException("Not implemented");
437 }
438
439 @Override
440 public int latToY(double lat, int zoom) {
441 throw new UnsupportedOperationException("Not implemented");
442 }
443
444 @Override
445 public double XToLon(int x, int zoom) {
446 throw new UnsupportedOperationException("Not implemented");
447 }
448
449 @Override
450 public double YToLat(int y, int zoom) {
451 throw new UnsupportedOperationException("Not implemented");
452 }
453
454 @Override
455 public double latToTileY(double lat, int zoom) {
456 throw new UnsupportedOperationException("Not implemented");
457 }
458
459 @Override
460 public ICoordinate tileXYToLatLon(Tile tile) {
461 return tileXYToLatLon(tile.getXtile(), tile.getYtile(), tile.getZoom());
462 }
463
464 @Override
465 public ICoordinate tileXYToLatLon(TileXY xy, int zoom) {
466 return tileXYToLatLon(xy.getXIndex(), xy.getYIndex(), zoom);
467 }
468
469 @Override
470 public ICoordinate tileXYToLatLon(int x, int y, int zoom) {
471 TileMatrix matrix = getTileMatrix(zoom);
472 if (matrix == null) {
473 return Main.getProjection().getWorldBoundsLatLon().getCenter().toCoordinate();
474 }
475 double scale = matrix.scaleDenominator * this.crsScale;
476 EastNorth ret = new EastNorth(matrix.topLeftCorner.east() + x * scale, matrix.topLeftCorner.north() - y * scale);
477 return Main.getProjection().eastNorth2latlon(ret).toCoordinate();
478 }
479
480 @Override
481 public TileXY latLonToTileXY(double lat, double lon, int zoom) {
482 TileMatrix matrix = getTileMatrix(zoom);
483 if (matrix == null) {
484 return new TileXY(0, 0);
485 }
486
487 Projection proj = Main.getProjection();
488 EastNorth enPoint = proj.latlon2eastNorth(new LatLon(lat, lon));
489 double scale = matrix.scaleDenominator * this.crsScale;
490 return new TileXY(
491 (enPoint.east() - matrix.topLeftCorner.east()) / scale,
492 (matrix.topLeftCorner.north() - enPoint.north()) / scale
493 );
494 }
495
496 @Override
497 public TileXY latLonToTileXY(ICoordinate point, int zoom) {
498 return latLonToTileXY(point.getLat(), point.getLon(), zoom);
499 }
500
501 @Override
502 public int getTileXMax(int zoom) {
503 return getTileXMax(zoom, Main.getProjection());
504 }
505
506 @Override
507 public int getTileXMin(int zoom) {
508 return 0;
509 }
510
511 @Override
512 public int getTileYMax(int zoom) {
513 return getTileYMax(zoom, Main.getProjection());
514 }
515
516 @Override
517 public int getTileYMin(int zoom) {
518 return 0;
519 }
520
521 @Override
522 public Point latLonToXY(double lat, double lon, int zoom) {
523 TileMatrix matrix = getTileMatrix(zoom);
524 if (matrix == null) {
525 return new Point(0, 0);
526 }
527 double scale = matrix.scaleDenominator * this.crsScale;
528 EastNorth point = Main.getProjection().latlon2eastNorth(new LatLon(lat, lon));
529 return new Point(
530 (int) Math.round((point.east() - matrix.topLeftCorner.east()) / scale),
531 (int) Math.round((matrix.topLeftCorner.north() - point.north()) / scale)
532 );
533 }
534
535 @Override
536 public Point latLonToXY(ICoordinate point, int zoom) {
537 return latLonToXY(point.getLat(), point.getLon(), zoom);
538 }
539
540 @Override
541 public Coordinate xyToLatLon(Point point, int zoom) {
542 return xyToLatLon(point.x, point.y, zoom);
543 }
544
545 @Override
546 public Coordinate xyToLatLon(int x, int y, int zoom) {
547 TileMatrix matrix = getTileMatrix(zoom);
548 if (matrix == null) {
549 return new Coordinate(0, 0);
550 }
551 double scale = matrix.scaleDenominator * this.crsScale;
552 Projection proj = Main.getProjection();
553 EastNorth ret = new EastNorth(
554 matrix.topLeftCorner.east() + x * scale,
555 matrix.topLeftCorner.north() - y * scale
556 );
557 LatLon ll = proj.eastNorth2latlon(ret);
558 return new Coordinate(ll.lat(), ll.lon());
559 }
560
561 @Override
562 public double lonToTileX(double lon, int zoom) {
563 throw new UnsupportedOperationException("Not implemented");
564 }
565
566 @Override
567 public double tileXToLon(int x, int zoom) {
568 throw new UnsupportedOperationException("Not implemented");
569 }
570
571 @Override
572 public double tileYToLat(int y, int zoom) {
573 throw new UnsupportedOperationException("Not implemented");
574 }
575
576 @Override
577 public Map<String, String> getHeaders() {
578 return headers;
579 }
580
581 @Override
582 public int getMaxZoom() {
583 if (this.currentTileMatrixSet != null) {
584 return this.currentTileMatrixSet.tileMatrix.size();
585 }
586 return 0;
587 }
588
589 @Override
590 public String getTileId(int zoom, int tilex, int tiley) {
591 return getTileUrl(zoom, tilex, tiley);
592 }
593
594
595 /**
596 * Checks if url is acceptable by this Tile Source
597 * @param url URL to check
598 */
599 public static void checkUrl(String url) {
600 CheckParameterUtil.ensureParameterNotNull(url, "url");
601 Matcher m = Pattern.compile("\\{[^}]*\\}").matcher(url);
602 while (m.find()) {
603 boolean isSupportedPattern = false;
604 for (String pattern : ALL_PATTERNS) {
605 if (m.group().matches(pattern)) {
606 isSupportedPattern = true;
607 break;
608 }
609 }
610 if (!isSupportedPattern) {
611 throw new IllegalArgumentException(
612 tr("{0} is not a valid WMS argument. Please check this server URL:\n{1}", m.group(), url));
613 }
614 }
615 }
616
617 /**
618 * @return set of projection codes that this TileSource supports
619 */
620 public Set<String> getSupportedProjections() {
621 return this.currentLayer.tileMatrixSetByCRS.keySet();
622 }
623
624 private int getTileYMax(int zoom, Projection proj) {
625 TileMatrix matrix = getTileMatrix(zoom);
626 if (matrix == null) {
627 return 0;
628 }
629
630 if (matrix.matrixHeight != -1) {
631 return matrix.matrixHeight;
632 }
633
634 double scale = matrix.scaleDenominator * this.crsScale;
635 EastNorth min = matrix.topLeftCorner;
636 EastNorth max = proj.latlon2eastNorth(proj.getWorldBoundsLatLon().getMax());
637 return (int) Math.ceil(Math.abs(max.north() - min.north()) / scale);
638 }
639
640 private int getTileXMax(int zoom, Projection proj) {
641 TileMatrix matrix = getTileMatrix(zoom);
642 if (matrix == null) {
643 return 0;
644 }
645 if (matrix.matrixWidth != -1) {
646 return matrix.matrixWidth;
647 }
648
649 double scale = matrix.scaleDenominator * this.crsScale;
650 EastNorth min = matrix.topLeftCorner;
651 EastNorth max = proj.latlon2eastNorth(proj.getWorldBoundsLatLon().getMax());
652 return (int) Math.ceil(Math.abs(max.east() - min.east()) / scale);
653 }
654}
Note: See TracBrowser for help on using the repository browser.