source: josm/trunk/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java@ 18478

Last change on this file since 18478 was 18478, checked in by taylor.smock, 23 months ago

Really fix failing tests in VectorDataSetTest

The test failed ~50% of the time (it depended upon the HashSet ordering).
We now use an ArrayList instead, and a the optimization that caused the
failure is not as significant anymore (node search is significantly cheaper
since r18465).

File size: 5.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
3
4import java.awt.image.BufferedImage;
5import java.io.IOException;
6import java.io.InputStream;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.List;
10import java.util.Objects;
11
12import org.openstreetmap.gui.jmapviewer.Tile;
13import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
14import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
15import org.openstreetmap.josm.data.IQuadBucketType;
16import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
17import org.openstreetmap.josm.data.osm.BBox;
18import org.openstreetmap.josm.data.protobuf.ProtobufParser;
19import org.openstreetmap.josm.data.protobuf.ProtobufRecord;
20import org.openstreetmap.josm.data.vector.VectorDataStore;
21import org.openstreetmap.josm.tools.ListenerList;
22import org.openstreetmap.josm.tools.Logging;
23
24/**
25 * A class for Mapbox Vector Tiles
26 *
27 * @author Taylor Smock
28 * @since 17862
29 */
30public class MVTTile extends Tile implements VectorTile, IQuadBucketType {
31 private final ListenerList<TileListener> listenerList = ListenerList.create();
32 private Collection<Layer> layers;
33 private int extent = Layer.DEFAULT_EXTENT;
34 static final BufferedImage CLEAR_LOADED = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
35 private BBox bbox;
36 private VectorDataStore vectorDataStore;
37
38 /**
39 * Create a new Tile
40 * @param source The source of the tile
41 * @param xtile The x coordinate for the tile
42 * @param ytile The y coordinate for the tile
43 * @param zoom The zoom for the tile
44 */
45 public MVTTile(TileSource source, int xtile, int ytile, int zoom) {
46 super(source, xtile, ytile, zoom);
47 }
48
49 @Override
50 public void loadImage(final InputStream inputStream) throws IOException {
51 if (this.image == null || this.image == Tile.LOADING_IMAGE || this.image == Tile.ERROR_IMAGE) {
52 this.initLoading();
53 ProtobufParser parser = new ProtobufParser(inputStream);
54 Collection<ProtobufRecord> protobufRecords = parser.allRecords();
55 this.layers = new ArrayList<>(protobufRecords.size());
56 for (ProtobufRecord protoBufRecord : protobufRecords) {
57 if (protoBufRecord.getField() == Layer.LAYER_FIELD) {
58 try (ProtobufParser tParser = new ProtobufParser(protoBufRecord.getBytes())) {
59 this.layers.add(new Layer(tParser.allRecords()));
60 } catch (IOException e) {
61 Logging.error(e);
62 } finally {
63 // Cleanup bytes
64 protoBufRecord.close();
65 }
66 }
67 }
68 this.layers = new ArrayList<>(this.layers);
69
70 this.extent = layers.stream().filter(Objects::nonNull).mapToInt(Layer::getExtent).max().orElse(Layer.DEFAULT_EXTENT);
71 if (this.getData() != null) {
72 this.finishLoading();
73 this.listenerList.fireEvent(event -> event.finishedLoading(this));
74 // Ensure that we don't keep the loading image around
75 this.image = CLEAR_LOADED;
76 // Cleanup as much as possible -- layers will still exist, but only base information (like name, extent) will remain.
77 // Called last just in case the listeners need the layers.
78 this.layers.forEach(Layer::destroy);
79 }
80 }
81 }
82
83 @Override
84 public Collection<Layer> getLayers() {
85 return this.layers;
86 }
87
88 @Override
89 public int getExtent() {
90 return this.extent;
91 }
92
93 /**
94 * Add a tile loader finisher listener
95 *
96 * @param listener The listener to add
97 */
98 public void addTileLoaderFinisher(TileListener listener) {
99 // Add as weak listeners since we don't want to keep unnecessary references.
100 this.listenerList.addWeakListener(listener);
101 }
102
103 @Override
104 public BBox getBBox() {
105 if (this.bbox == null) {
106 final ICoordinate upperLeft = this.getTileSource().tileXYToLatLon(this);
107 final ICoordinate lowerRight = this.getTileSource()
108 .tileXYToLatLon(this.getXtile() + 1, this.getYtile() + 1, this.getZoom());
109 BBox newBBox = new BBox(upperLeft.getLon(), upperLeft.getLat(), lowerRight.getLon(), lowerRight.getLat());
110 this.bbox = newBBox.toImmutable();
111 }
112 return this.bbox;
113 }
114
115 /**
116 * Get the datastore for this tile
117 * @return The data
118 */
119 public VectorDataStore getData() {
120 if (this.vectorDataStore == null) {
121 VectorDataStore newDataStore = new VectorDataStore();
122 newDataStore.addDataTile(this);
123 this.vectorDataStore = newDataStore;
124 }
125 return this.vectorDataStore;
126 }
127
128 /**
129 * A class that can be notified that a tile has finished loading
130 *
131 * @author Taylor Smock
132 */
133 public interface TileListener {
134 /**
135 * Called when the MVTTile is finished loading
136 *
137 * @param tile The tile that finished loading
138 */
139 void finishedLoading(MVTTile tile);
140 }
141
142 /**
143 * A class used to set the layers that an MVTTile will show.
144 *
145 * @author Taylor Smock
146 */
147 public interface LayerShower {
148 /**
149 * Get a list of layers to show
150 *
151 * @return A list of layer names
152 */
153 List<String> layersToShow();
154 }
155}
Note: See TracBrowser for help on using the repository browser.