source: josm/trunk/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java@ 17862

Last change on this file since 17862 was 17862, checked in by simon04, 3 years ago

fix #17177 - Add support for Mapbox Vector Tile (patch by taylor.smock)

Signed-off-by: Taylor Smock <tsmock@…>

File size: 6.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
3
4import java.io.IOException;
5import java.text.NumberFormat;
6import java.util.ArrayList;
7import java.util.List;
8
9import org.openstreetmap.josm.data.osm.TagMap;
10import org.openstreetmap.josm.data.protobuf.ProtobufPacked;
11import org.openstreetmap.josm.data.protobuf.ProtobufParser;
12import org.openstreetmap.josm.data.protobuf.ProtobufRecord;
13import org.openstreetmap.josm.tools.Utils;
14
15/**
16 * A Feature for a {@link Layer}
17 *
18 * @author Taylor Smock
19 * @since xxx
20 */
21public class Feature {
22 private static final byte ID_FIELD = 1;
23 private static final byte TAG_FIELD = 2;
24 private static final byte GEOMETRY_TYPE_FIELD = 3;
25 private static final byte GEOMETRY_FIELD = 4;
26 /**
27 * The geometry of the feature. Required.
28 */
29 private final List<CommandInteger> geometry = new ArrayList<>();
30
31 /**
32 * The geometry type of the feature. Required.
33 */
34 private final GeometryTypes geometryType;
35 /**
36 * The id of the feature. Optional.
37 */
38 // Technically, uint64
39 private final long id;
40 /**
41 * The tags of the feature. Optional.
42 */
43 private TagMap tags;
44 private Geometry geometryObject;
45
46 /**
47 * Create a new Feature
48 *
49 * @param layer The layer the feature is part of (required for tags)
50 * @param record The record to create the feature from
51 * @throws IOException - if an IO error occurs
52 */
53 public Feature(Layer layer, ProtobufRecord record) throws IOException {
54 long tId = 0;
55 GeometryTypes geometryTypeTemp = GeometryTypes.UNKNOWN;
56 String key = null;
57 try (ProtobufParser parser = new ProtobufParser(record.getBytes())) {
58 while (parser.hasNext()) {
59 try (ProtobufRecord next = new ProtobufRecord(parser)) {
60 if (next.getField() == TAG_FIELD) {
61 if (tags == null) {
62 tags = new TagMap();
63 }
64 // This is packed in v1 and v2
65 ProtobufPacked packed = new ProtobufPacked(next.getBytes());
66 for (Number number : packed.getArray()) {
67 key = parseTagValue(key, layer, number);
68 }
69 } else if (next.getField() == GEOMETRY_FIELD) {
70 // This is packed in v1 and v2
71 ProtobufPacked packed = new ProtobufPacked(next.getBytes());
72 CommandInteger currentCommand = null;
73 for (Number number : packed.getArray()) {
74 if (currentCommand != null && currentCommand.hasAllExpectedParameters()) {
75 currentCommand = null;
76 }
77 if (currentCommand == null) {
78 currentCommand = new CommandInteger(number.intValue());
79 this.geometry.add(currentCommand);
80 } else {
81 currentCommand.addParameter(ProtobufParser.decodeZigZag(number));
82 }
83 }
84 // TODO fallback to non-packed
85 } else if (next.getField() == GEOMETRY_TYPE_FIELD) {
86 geometryTypeTemp = GeometryTypes.values()[next.asUnsignedVarInt().intValue()];
87 } else if (next.getField() == ID_FIELD) {
88 tId = next.asUnsignedVarInt().longValue();
89 }
90 }
91 }
92 }
93 this.id = tId;
94 this.geometryType = geometryTypeTemp;
95 record.close();
96 }
97
98 /**
99 * Parse a tag value
100 *
101 * @param key The current key (or {@code null}, if {@code null}, the returned value will be the new key)
102 * @param layer The layer with key/value information
103 * @param number The number to get the value from
104 * @return The new key (if {@code null}, then a value was parsed and added to tags)
105 */
106 private String parseTagValue(String key, Layer layer, Number number) {
107 if (key == null) {
108 key = layer.getKey(number.intValue());
109 } else {
110 Object value = layer.getValue(number.intValue());
111 if (value instanceof Double || value instanceof Float) {
112 // reset grouping if the instance is a singleton
113 final NumberFormat numberFormat = NumberFormat.getNumberInstance();
114 final boolean grouping = numberFormat.isGroupingUsed();
115 try {
116 numberFormat.setGroupingUsed(false);
117 this.tags.put(key, numberFormat.format(value));
118 } finally {
119 numberFormat.setGroupingUsed(grouping);
120 }
121 } else {
122 this.tags.put(key, Utils.intern(value.toString()));
123 }
124 key = null;
125 }
126 return key;
127 }
128
129 /**
130 * Get the geometry instructions
131 *
132 * @return The geometry
133 */
134 public List<CommandInteger> getGeometry() {
135 return this.geometry;
136 }
137
138 /**
139 * Get the geometry type
140 *
141 * @return The {@link GeometryTypes}
142 */
143 public GeometryTypes getGeometryType() {
144 return this.geometryType;
145 }
146
147 /**
148 * Get the id of the object
149 *
150 * @return The unique id in the layer, or 0.
151 */
152 public long getId() {
153 return this.id;
154 }
155
156 /**
157 * Get the tags
158 *
159 * @return A tag map
160 */
161 public TagMap getTags() {
162 return this.tags;
163 }
164
165 /**
166 * Get the an object with shapes for the geometry
167 * @return An object with usable geometry information
168 */
169 public Geometry getGeometryObject() {
170 if (this.geometryObject == null) {
171 this.geometryObject = new Geometry(this.getGeometryType(), this.getGeometry());
172 }
173 return this.geometryObject;
174 }
175}
Note: See TracBrowser for help on using the repository browser.