1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
---|
3 |
|
---|
4 | import java.awt.image.BufferedImage;
|
---|
5 | import java.io.IOException;
|
---|
6 | import java.io.InputStream;
|
---|
7 | import java.util.ArrayList;
|
---|
8 | import java.util.Collection;
|
---|
9 | import java.util.List;
|
---|
10 | import java.util.Objects;
|
---|
11 |
|
---|
12 | import org.openstreetmap.gui.jmapviewer.Tile;
|
---|
13 | import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
---|
14 | import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
|
---|
15 | import org.openstreetmap.josm.data.IQuadBucketType;
|
---|
16 | import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
---|
17 | import org.openstreetmap.josm.data.osm.BBox;
|
---|
18 | import org.openstreetmap.josm.data.protobuf.ProtobufParser;
|
---|
19 | import org.openstreetmap.josm.data.protobuf.ProtobufRecord;
|
---|
20 | import org.openstreetmap.josm.data.vector.VectorDataStore;
|
---|
21 | import org.openstreetmap.josm.tools.ListenerList;
|
---|
22 | import org.openstreetmap.josm.tools.Logging;
|
---|
23 |
|
---|
24 | /**
|
---|
25 | * A class for Mapbox Vector Tiles
|
---|
26 | *
|
---|
27 | * @author Taylor Smock
|
---|
28 | * @since 17862
|
---|
29 | */
|
---|
30 | public 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 | }
|
---|