| 1 | From 900593d0f434dc3ca92919ae2104043a402d0e8e Mon Sep 17 00:00:00 2001
|
|---|
| 2 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 3 | Date: Thu, 8 Apr 2021 13:37:54 -0600
|
|---|
| 4 | Subject: [PATCH 01/50] Protobuf: Initial implementation
|
|---|
| 5 |
|
|---|
| 6 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 7 | ---
|
|---|
| 8 | .../josm/data/protobuf/ProtoBufPacked.java | 62 +++++
|
|---|
| 9 | .../josm/data/protobuf/ProtoBufParser.java | 245 ++++++++++++++++++
|
|---|
| 10 | .../josm/data/protobuf/ProtoBufRecord.java | 152 +++++++++++
|
|---|
| 11 | .../josm/data/protobuf/WireType.java | 61 +++++
|
|---|
| 12 | test/data/pbf/openinframap/17/26028/50060.pbf | Bin 0 -> 1082 bytes
|
|---|
| 13 | .../data/protobuf/ProtoBufParserTest.java | 51 ++++
|
|---|
| 14 | .../data/protobuf/ProtoBufRecordTest.java | 30 +++
|
|---|
| 15 | .../josm/data/protobuf/ProtoBufTest.java | 211 +++++++++++++++
|
|---|
| 16 | 8 files changed, 812 insertions(+)
|
|---|
| 17 | create mode 100644 src/org/openstreetmap/josm/data/protobuf/ProtoBufPacked.java
|
|---|
| 18 | create mode 100644 src/org/openstreetmap/josm/data/protobuf/ProtoBufParser.java
|
|---|
| 19 | create mode 100644 src/org/openstreetmap/josm/data/protobuf/ProtoBufRecord.java
|
|---|
| 20 | create mode 100644 src/org/openstreetmap/josm/data/protobuf/WireType.java
|
|---|
| 21 | create mode 100644 test/data/pbf/openinframap/17/26028/50060.pbf
|
|---|
| 22 | create mode 100644 test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java
|
|---|
| 23 | create mode 100644 test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufRecordTest.java
|
|---|
| 24 | create mode 100644 test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 25 |
|
|---|
| 26 | diff --git a/src/org/openstreetmap/josm/data/protobuf/ProtoBufPacked.java b/src/org/openstreetmap/josm/data/protobuf/ProtoBufPacked.java
|
|---|
| 27 | new file mode 100644
|
|---|
| 28 | index 000000000..109f8915a
|
|---|
| 29 | --- /dev/null
|
|---|
| 30 | +++ b/src/org/openstreetmap/josm/data/protobuf/ProtoBufPacked.java
|
|---|
| 31 | @@ -0,0 +1,62 @@
|
|---|
| 32 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 33 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 34 | +
|
|---|
| 35 | +import java.util.ArrayList;
|
|---|
| 36 | +import java.util.List;
|
|---|
| 37 | +
|
|---|
| 38 | +/**
|
|---|
| 39 | + * Parse packed values (only numerical values)
|
|---|
| 40 | + *
|
|---|
| 41 | + * @author Taylor Smock
|
|---|
| 42 | + * @since xxx
|
|---|
| 43 | + */
|
|---|
| 44 | +public class ProtoBufPacked {
|
|---|
| 45 | + private final byte[] bytes;
|
|---|
| 46 | + private final Number[] numbers;
|
|---|
| 47 | + private int location;
|
|---|
| 48 | +
|
|---|
| 49 | + /**
|
|---|
| 50 | + * Create a new ProtoBufPacked object
|
|---|
| 51 | + *
|
|---|
| 52 | + * @param bytes The packed bytes
|
|---|
| 53 | + */
|
|---|
| 54 | + public ProtoBufPacked(byte[] bytes) {
|
|---|
| 55 | + this.location = 0;
|
|---|
| 56 | + this.bytes = bytes;
|
|---|
| 57 | + List<Number> numbersT = new ArrayList<>();
|
|---|
| 58 | + while (this.location < bytes.length) {
|
|---|
| 59 | + numbersT.add(ProtoBufParser.convertByteArray(this.nextVarInt(), ProtoBufParser.VAR_INT_BYTE_SIZE));
|
|---|
| 60 | + }
|
|---|
| 61 | +
|
|---|
| 62 | + this.numbers = new Number[numbersT.size()];
|
|---|
| 63 | + for (int i = 0; i < numbersT.size(); i++) {
|
|---|
| 64 | + this.numbers[i] = numbersT.get(i);
|
|---|
| 65 | + }
|
|---|
| 66 | + }
|
|---|
| 67 | +
|
|---|
| 68 | + /**
|
|---|
| 69 | + * Get the parsed number array
|
|---|
| 70 | + *
|
|---|
| 71 | + * @return The number array
|
|---|
| 72 | + */
|
|---|
| 73 | + public Number[] getArray() {
|
|---|
| 74 | + return this.numbers;
|
|---|
| 75 | + }
|
|---|
| 76 | +
|
|---|
| 77 | + private byte[] nextVarInt() {
|
|---|
| 78 | + List<Byte> byteList = new ArrayList<>();
|
|---|
| 79 | + while ((this.bytes[this.location] & ProtoBufParser.MOST_SIGNIFICANT_BYTE)
|
|---|
| 80 | + == ProtoBufParser.MOST_SIGNIFICANT_BYTE) {
|
|---|
| 81 | + // Get rid of the leading bit (shift left 1, then shift right 1 unsigned)
|
|---|
| 82 | + byteList.add((byte) (this.bytes[this.location++] ^ ProtoBufParser.MOST_SIGNIFICANT_BYTE));
|
|---|
| 83 | + }
|
|---|
| 84 | + // The last byte doesn't drop the most significant bit
|
|---|
| 85 | + byteList.add(this.bytes[this.location++]);
|
|---|
| 86 | + byte[] byteArray = new byte[byteList.size()];
|
|---|
| 87 | + for (int i = 0; i < byteList.size(); i++) {
|
|---|
| 88 | + byteArray[i] = byteList.get(i);
|
|---|
| 89 | + }
|
|---|
| 90 | +
|
|---|
| 91 | + return byteArray;
|
|---|
| 92 | + }
|
|---|
| 93 | +}
|
|---|
| 94 | diff --git a/src/org/openstreetmap/josm/data/protobuf/ProtoBufParser.java b/src/org/openstreetmap/josm/data/protobuf/ProtoBufParser.java
|
|---|
| 95 | new file mode 100644
|
|---|
| 96 | index 000000000..18059e339
|
|---|
| 97 | --- /dev/null
|
|---|
| 98 | +++ b/src/org/openstreetmap/josm/data/protobuf/ProtoBufParser.java
|
|---|
| 99 | @@ -0,0 +1,245 @@
|
|---|
| 100 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 101 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 102 | +
|
|---|
| 103 | +import java.io.BufferedInputStream;
|
|---|
| 104 | +import java.io.ByteArrayInputStream;
|
|---|
| 105 | +import java.io.IOException;
|
|---|
| 106 | +import java.io.InputStream;
|
|---|
| 107 | +import java.util.ArrayList;
|
|---|
| 108 | +import java.util.Collection;
|
|---|
| 109 | +import java.util.List;
|
|---|
| 110 | +
|
|---|
| 111 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 112 | +
|
|---|
| 113 | +/**
|
|---|
| 114 | + * A basic Protobuf parser
|
|---|
| 115 | + *
|
|---|
| 116 | + * @author Taylor Smock
|
|---|
| 117 | + * @since xxx
|
|---|
| 118 | + */
|
|---|
| 119 | +public class ProtoBufParser implements AutoCloseable {
|
|---|
| 120 | + /**
|
|---|
| 121 | + * The default byte size (see {@link #VAR_INT_BYTE_SIZE} for var ints)
|
|---|
| 122 | + */
|
|---|
| 123 | + public static final byte BYTE_SIZE = 8;
|
|---|
| 124 | + /**
|
|---|
| 125 | + * The byte size for var ints (since the first byte is just an indicator for if the var int is done)
|
|---|
| 126 | + */
|
|---|
| 127 | + public static final byte VAR_INT_BYTE_SIZE = BYTE_SIZE - 1;
|
|---|
| 128 | + /**
|
|---|
| 129 | + * Used to get the most significant byte
|
|---|
| 130 | + */
|
|---|
| 131 | + static final byte MOST_SIGNIFICANT_BYTE = (byte) (1 << 7);
|
|---|
| 132 | + /**
|
|---|
| 133 | + * Convert a byte array to a number (little endian)
|
|---|
| 134 | + *
|
|---|
| 135 | + * @param bytes The bytes to convert
|
|---|
| 136 | + * @param byteSize The size of the byte. For var ints, this is 7, for other ints, this is 8.
|
|---|
| 137 | + * @return An appropriate {@link Number} class.
|
|---|
| 138 | + */
|
|---|
| 139 | + public static Number convertByteArray(byte[] bytes, byte byteSize) {
|
|---|
| 140 | + long number = 0;
|
|---|
| 141 | + for (int i = 0; i < bytes.length; i++) {
|
|---|
| 142 | + // Need to convert to uint64 in order to avoid bit operation from filling in 1's and overflow issues
|
|---|
| 143 | + number += Byte.toUnsignedLong(bytes[i]) << (byteSize * i);
|
|---|
| 144 | + }
|
|---|
| 145 | + return convertLong(number);
|
|---|
| 146 | + }
|
|---|
| 147 | +
|
|---|
| 148 | + /**
|
|---|
| 149 | + * Convert a long to an appropriate {@link Number} class
|
|---|
| 150 | + *
|
|---|
| 151 | + * @param number The long to convert
|
|---|
| 152 | + * @return A {@link Number}
|
|---|
| 153 | + */
|
|---|
| 154 | + public static Number convertLong(long number) {
|
|---|
| 155 | + // TODO deal with booleans
|
|---|
| 156 | + if (number <= Byte.MAX_VALUE && number >= Byte.MIN_VALUE) {
|
|---|
| 157 | + return (byte) number;
|
|---|
| 158 | + } else if (number <= Short.MAX_VALUE && number >= Short.MIN_VALUE) {
|
|---|
| 159 | + return (short) number;
|
|---|
| 160 | + } else if (number <= Integer.MAX_VALUE && number >= Integer.MIN_VALUE) {
|
|---|
| 161 | + return (int) number;
|
|---|
| 162 | + }
|
|---|
| 163 | + return number;
|
|---|
| 164 | + }
|
|---|
| 165 | +
|
|---|
| 166 | + /**
|
|---|
| 167 | + * Decode a zig-zag encoded value
|
|---|
| 168 | + *
|
|---|
| 169 | + * @param signed The value to decode
|
|---|
| 170 | + * @return The decoded value
|
|---|
| 171 | + */
|
|---|
| 172 | + public static Number decodeZigZag(Number signed) {
|
|---|
| 173 | + final long value = signed.longValue();
|
|---|
| 174 | + return convertLong((value >> 1) ^ -(value & 1));
|
|---|
| 175 | + }
|
|---|
| 176 | +
|
|---|
| 177 | + /**
|
|---|
| 178 | + * Encode a number to a zig-zag encode value
|
|---|
| 179 | + *
|
|---|
| 180 | + * @param signed The number to encode
|
|---|
| 181 | + * @return The encoded value
|
|---|
| 182 | + */
|
|---|
| 183 | + public static Number encodeZigZag(Number signed) {
|
|---|
| 184 | + final long value = signed.longValue();
|
|---|
| 185 | + // This boundary condition could be >= or <= or both. Tests indicate that it doesn't actually matter.
|
|---|
| 186 | + // The only difference would be the number type returned, except it is always converted to the most basic type.
|
|---|
| 187 | + final int shift = (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE ? Long.BYTES : Integer.BYTES) * 8 - 1;
|
|---|
| 188 | + return convertLong((value << 1) ^ (value >> shift));
|
|---|
| 189 | + }
|
|---|
| 190 | +
|
|---|
| 191 | + private final InputStream inputStream;
|
|---|
| 192 | +
|
|---|
| 193 | + /**
|
|---|
| 194 | + * Create a new parser
|
|---|
| 195 | + *
|
|---|
| 196 | + * @param bytes The bytes to parse
|
|---|
| 197 | + */
|
|---|
| 198 | + public ProtoBufParser(byte[] bytes) {
|
|---|
| 199 | + this(new ByteArrayInputStream(bytes));
|
|---|
| 200 | + }
|
|---|
| 201 | +
|
|---|
| 202 | + /**
|
|---|
| 203 | + * Create a new parser
|
|---|
| 204 | + *
|
|---|
| 205 | + * @param inputStream The InputStream (will be fully read at this time)
|
|---|
| 206 | + */
|
|---|
| 207 | + public ProtoBufParser(InputStream inputStream) {
|
|---|
| 208 | + if (inputStream.markSupported()) {
|
|---|
| 209 | + this.inputStream = inputStream;
|
|---|
| 210 | + } else {
|
|---|
| 211 | + this.inputStream = new BufferedInputStream(inputStream);
|
|---|
| 212 | + }
|
|---|
| 213 | + }
|
|---|
| 214 | +
|
|---|
| 215 | + /**
|
|---|
| 216 | + * Read all records
|
|---|
| 217 | + *
|
|---|
| 218 | + * @return A collection of all records
|
|---|
| 219 | + * @throws IOException - if an IO error occurs
|
|---|
| 220 | + */
|
|---|
| 221 | + public Collection<ProtoBufRecord> allRecords() throws IOException {
|
|---|
| 222 | + Collection<ProtoBufRecord> records = new ArrayList<>();
|
|---|
| 223 | + while (this.hasNext()) {
|
|---|
| 224 | + records.add(new ProtoBufRecord(this));
|
|---|
| 225 | + }
|
|---|
| 226 | + return records;
|
|---|
| 227 | + }
|
|---|
| 228 | +
|
|---|
| 229 | + @Override
|
|---|
| 230 | + public void close() {
|
|---|
| 231 | + try {
|
|---|
| 232 | + this.inputStream.close();
|
|---|
| 233 | + } catch (IOException e) {
|
|---|
| 234 | + Logging.error(e);
|
|---|
| 235 | + }
|
|---|
| 236 | + }
|
|---|
| 237 | +
|
|---|
| 238 | + /**
|
|---|
| 239 | + * Check if there is more data to read
|
|---|
| 240 | + *
|
|---|
| 241 | + * @return {@code true} if there is more data to read
|
|---|
| 242 | + * @throws IOException - if an IO error occurs
|
|---|
| 243 | + */
|
|---|
| 244 | + public boolean hasNext() throws IOException {
|
|---|
| 245 | + return this.inputStream.available() > 0;
|
|---|
| 246 | + }
|
|---|
| 247 | +
|
|---|
| 248 | + /**
|
|---|
| 249 | + * Get the "next" WireType
|
|---|
| 250 | + *
|
|---|
| 251 | + * @return {@link WireType} expected
|
|---|
| 252 | + * @throws IOException - if an IO error occurs
|
|---|
| 253 | + */
|
|---|
| 254 | + public WireType next() throws IOException {
|
|---|
| 255 | + this.inputStream.mark(16);
|
|---|
| 256 | + try {
|
|---|
| 257 | + return WireType.values()[this.inputStream.read() << 3];
|
|---|
| 258 | + } finally {
|
|---|
| 259 | + this.inputStream.reset();
|
|---|
| 260 | + }
|
|---|
| 261 | + }
|
|---|
| 262 | +
|
|---|
| 263 | + /**
|
|---|
| 264 | + * Get the next byte
|
|---|
| 265 | + *
|
|---|
| 266 | + * @return The next byte
|
|---|
| 267 | + * @throws IOException - if an IO error occurs
|
|---|
| 268 | + */
|
|---|
| 269 | + public int nextByte() throws IOException {
|
|---|
| 270 | + return this.inputStream.read();
|
|---|
| 271 | + }
|
|---|
| 272 | +
|
|---|
| 273 | + /**
|
|---|
| 274 | + * Get the next 32 bits ({@link WireType#THIRTY_TWO_BIT})
|
|---|
| 275 | + *
|
|---|
| 276 | + * @return a byte array of the next 32 bits (4 bytes)
|
|---|
| 277 | + * @throws IOException - if an IO error occurs
|
|---|
| 278 | + */
|
|---|
| 279 | + public byte[] nextFixed32() throws IOException {
|
|---|
| 280 | + // 4 bytes == 32 bits
|
|---|
| 281 | + return readNextBytes(4);
|
|---|
| 282 | + }
|
|---|
| 283 | +
|
|---|
| 284 | + /**
|
|---|
| 285 | + * Get the next 64 bits ({@link WireType#SIXTY_FOUR_BIT})
|
|---|
| 286 | + *
|
|---|
| 287 | + * @return a byte array of the next 64 bits (8 bytes)
|
|---|
| 288 | + * @throws IOException - if an IO error occurs
|
|---|
| 289 | + */
|
|---|
| 290 | + public byte[] nextFixed64() throws IOException {
|
|---|
| 291 | + // 8 bytes == 64 bits
|
|---|
| 292 | + return readNextBytes(8);
|
|---|
| 293 | + }
|
|---|
| 294 | +
|
|---|
| 295 | + /**
|
|---|
| 296 | + * Get the next delimited message ({@link WireType#LENGTH_DELIMITED})
|
|---|
| 297 | + *
|
|---|
| 298 | + * @return The next length delimited message
|
|---|
| 299 | + * @throws IOException - if an IO error occurs
|
|---|
| 300 | + */
|
|---|
| 301 | + public byte[] nextLengthDelimited() throws IOException {
|
|---|
| 302 | + int length = convertByteArray(this.nextVarInt(), VAR_INT_BYTE_SIZE).intValue();
|
|---|
| 303 | + return readNextBytes(length);
|
|---|
| 304 | + }
|
|---|
| 305 | +
|
|---|
| 306 | + /**
|
|---|
| 307 | + * Get the next var int ({@code WireType#VARINT})
|
|---|
| 308 | + *
|
|---|
| 309 | + * @return The next var int ({@code int32}, {@code int64}, {@code uint32}, {@code uint64}, {@code bool}, {@code enum})
|
|---|
| 310 | + * @throws IOException - if an IO error occurs
|
|---|
| 311 | + */
|
|---|
| 312 | + public byte[] nextVarInt() throws IOException {
|
|---|
| 313 | + List<Byte> byteList = new ArrayList<>();
|
|---|
| 314 | + int currentByte = this.nextByte();
|
|---|
| 315 | + while ((byte) (currentByte & MOST_SIGNIFICANT_BYTE) == MOST_SIGNIFICANT_BYTE && currentByte > 0) {
|
|---|
| 316 | + // Get rid of the leading bit (shift left 1, then shift right 1 unsigned)
|
|---|
| 317 | + byteList.add((byte) (currentByte ^ MOST_SIGNIFICANT_BYTE));
|
|---|
| 318 | + currentByte = this.nextByte();
|
|---|
| 319 | + }
|
|---|
| 320 | + // The last byte doesn't drop the most significant bit
|
|---|
| 321 | + byteList.add((byte) currentByte);
|
|---|
| 322 | + byte[] byteArray = new byte[byteList.size()];
|
|---|
| 323 | + for (int i = 0; i < byteList.size(); i++) {
|
|---|
| 324 | + byteArray[i] = byteList.get(i);
|
|---|
| 325 | + }
|
|---|
| 326 | +
|
|---|
| 327 | + return byteArray;
|
|---|
| 328 | + }
|
|---|
| 329 | +
|
|---|
| 330 | + /**
|
|---|
| 331 | + * Read an arbitrary number of bytes
|
|---|
| 332 | + *
|
|---|
| 333 | + * @param size The number of bytes to read
|
|---|
| 334 | + * @return a byte array of the specified size, filled with bytes read (unsigned)
|
|---|
| 335 | + * @throws IOException - if an IO error occurs
|
|---|
| 336 | + */
|
|---|
| 337 | + private byte[] readNextBytes(int size) throws IOException {
|
|---|
| 338 | + byte[] bytesRead = new byte[size];
|
|---|
| 339 | + for (int i = 0; i < bytesRead.length; i++) {
|
|---|
| 340 | + bytesRead[i] = (byte) this.nextByte();
|
|---|
| 341 | + }
|
|---|
| 342 | + return bytesRead;
|
|---|
| 343 | + }
|
|---|
| 344 | +}
|
|---|
| 345 | diff --git a/src/org/openstreetmap/josm/data/protobuf/ProtoBufRecord.java b/src/org/openstreetmap/josm/data/protobuf/ProtoBufRecord.java
|
|---|
| 346 | new file mode 100644
|
|---|
| 347 | index 000000000..1eb5d38a6
|
|---|
| 348 | --- /dev/null
|
|---|
| 349 | +++ b/src/org/openstreetmap/josm/data/protobuf/ProtoBufRecord.java
|
|---|
| 350 | @@ -0,0 +1,152 @@
|
|---|
| 351 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 352 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 353 | +
|
|---|
| 354 | +import java.io.IOException;
|
|---|
| 355 | +import java.nio.charset.StandardCharsets;
|
|---|
| 356 | +import java.util.stream.Stream;
|
|---|
| 357 | +
|
|---|
| 358 | +import org.openstreetmap.josm.tools.Utils;
|
|---|
| 359 | +
|
|---|
| 360 | +/**
|
|---|
| 361 | + * A protobuf record, storing the {@link WireType}, the parsed field number, and the bytes for it.
|
|---|
| 362 | + *
|
|---|
| 363 | + * @author Taylor Smock
|
|---|
| 364 | + * @since xxx
|
|---|
| 365 | + */
|
|---|
| 366 | +public class ProtoBufRecord implements AutoCloseable {
|
|---|
| 367 | + private static final byte[] EMPTY_BYTES = {};
|
|---|
| 368 | + private final WireType type;
|
|---|
| 369 | + private final int field;
|
|---|
| 370 | + private byte[] bytes;
|
|---|
| 371 | +
|
|---|
| 372 | + /**
|
|---|
| 373 | + * Create a new Protobuf record
|
|---|
| 374 | + *
|
|---|
| 375 | + * @param parser The parser to use to create the record
|
|---|
| 376 | + * @throws IOException - if an IO error occurs
|
|---|
| 377 | + */
|
|---|
| 378 | + public ProtoBufRecord(ProtoBufParser parser) throws IOException {
|
|---|
| 379 | + Number number = ProtoBufParser.convertByteArray(parser.nextVarInt(), ProtoBufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 380 | + // I don't foresee having field numbers > {@code Integer#MAX_VALUE >> 3}
|
|---|
| 381 | + this.field = (int) number.longValue() >> 3;
|
|---|
| 382 | + // 7 is 111 (so last three bits)
|
|---|
| 383 | + byte wireType = (byte) (number.longValue() & 7);
|
|---|
| 384 | + this.type = Stream.of(WireType.values()).filter(wType -> wType.getTypeRepresentation() == wireType).findFirst()
|
|---|
| 385 | + .orElse(WireType.UNKNOWN);
|
|---|
| 386 | +
|
|---|
| 387 | + if (this.type == WireType.VARINT) {
|
|---|
| 388 | + this.bytes = parser.nextVarInt();
|
|---|
| 389 | + } else if (this.type == WireType.SIXTY_FOUR_BIT) {
|
|---|
| 390 | + this.bytes = parser.nextFixed64();
|
|---|
| 391 | + } else if (this.type == WireType.THIRTY_TWO_BIT) {
|
|---|
| 392 | + this.bytes = parser.nextFixed32();
|
|---|
| 393 | + } else if (this.type == WireType.LENGTH_DELIMITED) {
|
|---|
| 394 | + this.bytes = parser.nextLengthDelimited();
|
|---|
| 395 | + } else {
|
|---|
| 396 | + this.bytes = EMPTY_BYTES;
|
|---|
| 397 | + }
|
|---|
| 398 | + }
|
|---|
| 399 | +
|
|---|
| 400 | + /**
|
|---|
| 401 | + * Get as a double ({@link WireType#SIXTY_FOUR_BIT})
|
|---|
| 402 | + *
|
|---|
| 403 | + * @return the double
|
|---|
| 404 | + */
|
|---|
| 405 | + public double asDouble() {
|
|---|
| 406 | + long doubleNumber = ProtoBufParser.convertByteArray(asFixed64(), ProtoBufParser.BYTE_SIZE).longValue();
|
|---|
| 407 | + return Double.longBitsToDouble(doubleNumber);
|
|---|
| 408 | + }
|
|---|
| 409 | +
|
|---|
| 410 | + /**
|
|---|
| 411 | + * Get as 32 bits ({@link WireType#THIRTY_TWO_BIT})
|
|---|
| 412 | + *
|
|---|
| 413 | + * @return a byte array of the 32 bits (4 bytes)
|
|---|
| 414 | + */
|
|---|
| 415 | + public byte[] asFixed32() {
|
|---|
| 416 | + // TODO verify, or just assume?
|
|---|
| 417 | + // 4 bytes == 32 bits
|
|---|
| 418 | + return this.bytes;
|
|---|
| 419 | + }
|
|---|
| 420 | +
|
|---|
| 421 | + /**
|
|---|
| 422 | + * Get as 64 bits ({@link WireType#SIXTY_FOUR_BIT})
|
|---|
| 423 | + *
|
|---|
| 424 | + * @return a byte array of the 64 bits (8 bytes)
|
|---|
| 425 | + */
|
|---|
| 426 | + public byte[] asFixed64() {
|
|---|
| 427 | + // TODO verify, or just assume?
|
|---|
| 428 | + // 8 bytes == 64 bits
|
|---|
| 429 | + return this.bytes;
|
|---|
| 430 | + }
|
|---|
| 431 | +
|
|---|
| 432 | + /**
|
|---|
| 433 | + * Get as a float ({@link WireType#THIRTY_TWO_BIT})
|
|---|
| 434 | + *
|
|---|
| 435 | + * @return the float
|
|---|
| 436 | + */
|
|---|
| 437 | + public float asFloat() {
|
|---|
| 438 | + int floatNumber = ProtoBufParser.convertByteArray(asFixed32(), ProtoBufParser.BYTE_SIZE).intValue();
|
|---|
| 439 | + return Float.intBitsToFloat(floatNumber);
|
|---|
| 440 | + }
|
|---|
| 441 | +
|
|---|
| 442 | + /**
|
|---|
| 443 | + * Get the signed var int ({@code WireType#VARINT}).
|
|---|
| 444 | + * These are specially encoded so that they take up less space.
|
|---|
| 445 | + *
|
|---|
| 446 | + * @return The signed var int ({@code sint32} or {@code sint64})
|
|---|
| 447 | + */
|
|---|
| 448 | + public Number asSignedVarInt() {
|
|---|
| 449 | + final Number signed = this.asUnsignedVarInt();
|
|---|
| 450 | + return ProtoBufParser.decodeZigZag(signed);
|
|---|
| 451 | + }
|
|---|
| 452 | +
|
|---|
| 453 | + /**
|
|---|
| 454 | + * Get as a string ({@link WireType#LENGTH_DELIMITED})
|
|---|
| 455 | + *
|
|---|
| 456 | + * @return The string (encoded as {@link StandardCharsets#UTF_8})
|
|---|
| 457 | + */
|
|---|
| 458 | + public String asString() {
|
|---|
| 459 | + return Utils.intern(new String(this.bytes, StandardCharsets.UTF_8));
|
|---|
| 460 | + }
|
|---|
| 461 | +
|
|---|
| 462 | + /**
|
|---|
| 463 | + * Get the var int ({@code WireType#VARINT})
|
|---|
| 464 | + *
|
|---|
| 465 | + * @return The var int ({@code int32}, {@code int64}, {@code uint32}, {@code uint64}, {@code bool}, {@code enum})
|
|---|
| 466 | + */
|
|---|
| 467 | + public Number asUnsignedVarInt() {
|
|---|
| 468 | + return ProtoBufParser.convertByteArray(this.bytes, ProtoBufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 469 | + }
|
|---|
| 470 | +
|
|---|
| 471 | + @Override
|
|---|
| 472 | + public void close() {
|
|---|
| 473 | + this.bytes = null;
|
|---|
| 474 | + }
|
|---|
| 475 | +
|
|---|
| 476 | + /**
|
|---|
| 477 | + * Get the raw bytes for this record
|
|---|
| 478 | + *
|
|---|
| 479 | + * @return The bytes
|
|---|
| 480 | + */
|
|---|
| 481 | + public byte[] getBytes() {
|
|---|
| 482 | + return this.bytes;
|
|---|
| 483 | + }
|
|---|
| 484 | +
|
|---|
| 485 | + /**
|
|---|
| 486 | + * Get the field value
|
|---|
| 487 | + *
|
|---|
| 488 | + * @return The field value
|
|---|
| 489 | + */
|
|---|
| 490 | + public int getField() {
|
|---|
| 491 | + return this.field;
|
|---|
| 492 | + }
|
|---|
| 493 | +
|
|---|
| 494 | + /**
|
|---|
| 495 | + * Get the WireType of the data
|
|---|
| 496 | + *
|
|---|
| 497 | + * @return The {@link WireType} of the data
|
|---|
| 498 | + */
|
|---|
| 499 | + public WireType getType() {
|
|---|
| 500 | + return this.type;
|
|---|
| 501 | + }
|
|---|
| 502 | +}
|
|---|
| 503 | diff --git a/src/org/openstreetmap/josm/data/protobuf/WireType.java b/src/org/openstreetmap/josm/data/protobuf/WireType.java
|
|---|
| 504 | new file mode 100644
|
|---|
| 505 | index 000000000..41edc8e4f
|
|---|
| 506 | --- /dev/null
|
|---|
| 507 | +++ b/src/org/openstreetmap/josm/data/protobuf/WireType.java
|
|---|
| 508 | @@ -0,0 +1,61 @@
|
|---|
| 509 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 510 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 511 | +
|
|---|
| 512 | +/**
|
|---|
| 513 | + * The WireTypes
|
|---|
| 514 | + *
|
|---|
| 515 | + * @author Taylor Smock
|
|---|
| 516 | + * @since xxx
|
|---|
| 517 | + */
|
|---|
| 518 | +public enum WireType {
|
|---|
| 519 | + /**
|
|---|
| 520 | + * int32, int64, uint32, uint64, sing32, sint64, bool, enum
|
|---|
| 521 | + */
|
|---|
| 522 | + VARINT(0),
|
|---|
| 523 | + /**
|
|---|
| 524 | + * fixed64, sfixed64, double
|
|---|
| 525 | + */
|
|---|
| 526 | + SIXTY_FOUR_BIT(1),
|
|---|
| 527 | + /**
|
|---|
| 528 | + * string, bytes, embedded messages, packed repeated fields
|
|---|
| 529 | + */
|
|---|
| 530 | + LENGTH_DELIMITED(2),
|
|---|
| 531 | + /**
|
|---|
| 532 | + * start groups
|
|---|
| 533 | + *
|
|---|
| 534 | + * @deprecated Unknown reason. Deprecated since at least 2012.
|
|---|
| 535 | + */
|
|---|
| 536 | + @Deprecated
|
|---|
| 537 | + START_GROUP(3),
|
|---|
| 538 | + /**
|
|---|
| 539 | + * end groups
|
|---|
| 540 | + *
|
|---|
| 541 | + * @deprecated Unknown reason. Deprecated since at least 2012.
|
|---|
| 542 | + */
|
|---|
| 543 | + @Deprecated
|
|---|
| 544 | + END_GROUP(4),
|
|---|
| 545 | + /**
|
|---|
| 546 | + * fixed32, sfixed32, float
|
|---|
| 547 | + */
|
|---|
| 548 | + THIRTY_TWO_BIT(5),
|
|---|
| 549 | +
|
|---|
| 550 | + /**
|
|---|
| 551 | + * For unknown WireTypes
|
|---|
| 552 | + */
|
|---|
| 553 | + UNKNOWN(Byte.MAX_VALUE);
|
|---|
| 554 | +
|
|---|
| 555 | + private final byte type;
|
|---|
| 556 | +
|
|---|
| 557 | + WireType(int value) {
|
|---|
| 558 | + this.type = (byte) value;
|
|---|
| 559 | + }
|
|---|
| 560 | +
|
|---|
| 561 | + /**
|
|---|
| 562 | + * Get the type representation (byte form)
|
|---|
| 563 | + *
|
|---|
| 564 | + * @return The wire type byte representation
|
|---|
| 565 | + */
|
|---|
| 566 | + public byte getTypeRepresentation() {
|
|---|
| 567 | + return this.type;
|
|---|
| 568 | + }
|
|---|
| 569 | +}
|
|---|
| 570 | diff --git a/test/data/pbf/openinframap/17/26028/50060.pbf b/test/data/pbf/openinframap/17/26028/50060.pbf
|
|---|
| 571 | new file mode 100644
|
|---|
| 572 | index 0000000000000000000000000000000000000000..358270e1b307f7373ec494adbd1ceb31c72e2956
|
|---|
| 573 | GIT binary patch
|
|---|
| 574 | literal 1082
|
|---|
| 575 | zcma)5Jx>%t7@pZZ?zlPRxV;mOA(+cna=*Y(pceiCXec!6xHrHiyEB`eSx*~tu>li<
|
|---|
| 576 | z5L>mjkXj2R#L`Y<<4386XkmznCKT$-&V9gzf+;rpem(E|JTos|LMY~Kns{NrD7IIF
|
|---|
| 577 | z7S79*F&g60ko!aioZ(y+9P>F7GD&^ybMuYudplduwJreHLckqqY(L+R&UYHOEbO3?
|
|---|
| 578 | z0hJos_@>Z=@rm??kQuHC%%aKRfEV+CNfW->!-_+~q%$`PoE(Gz)NOaRcsSR&Ja2s%
|
|---|
| 579 | zUp|0)@XBknylG#5RM^UaH>I*6Gd%SDolmbUJ+wi;hAO6chHm;gRn;SJSpYy-hwe!6
|
|---|
| 580 | z*~O2G1}#StO7O{;#c80<-qRnK2JjCcDjWP&%4k9)Pn;=*jx@Gq*>=v(I_xfNEOo^f
|
|---|
| 581 | zJWYIxbMkgfaUl>M<1ISWvebD0@Ymr#eV|)FBZ7w{aWSfAKVH1XCBMl-Ndn)CiMq?d
|
|---|
| 582 | zL<^{D23n&;MkMyK5~r+&QiQw1{9VL(p2|3tu3@E_8Nt>qR2yRCX;GYYs31l)r%X57
|
|---|
| 583 | zZ5KZ`zsxuHs{owaP#4zvSp((bCj5i&w-Nw%;n$L~*Wn9@-&FwUz_%4;gO{-Ry;rhw
|
|---|
| 584 | zpi98+5m(E&e%tuTBmj3FPwIp{m{}dDO{3!k6*7p3q%F@d!TF=gy2m6OkUW)|Mu~dw
|
|---|
| 585 | z>BxjIvBHz6_tg6lpGPreLSMV7siH9>o@fWsUo%b@%}5$jl`K5<iBcAry~}pe<jkO2
|
|---|
| 586 | zKc__`@qEf;uU`$F=?5bsx{EW7oS4dlhcu16IEblUVuN-TjSoU?8ipshMIZ;+Z6#!K
|
|---|
| 587 | G9OOSgfkw9g
|
|---|
| 588 |
|
|---|
| 589 | literal 0
|
|---|
| 590 | HcmV?d00001
|
|---|
| 591 |
|
|---|
| 592 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java
|
|---|
| 593 | new file mode 100644
|
|---|
| 594 | index 000000000..bdfdf86b7
|
|---|
| 595 | --- /dev/null
|
|---|
| 596 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java
|
|---|
| 597 | @@ -0,0 +1,51 @@
|
|---|
| 598 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 599 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 600 | +
|
|---|
| 601 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 602 | +
|
|---|
| 603 | +import org.junit.jupiter.api.Test;
|
|---|
| 604 | +
|
|---|
| 605 | +/**
|
|---|
| 606 | + * Test class for {@link ProtoBufParser}
|
|---|
| 607 | + * @author Taylor Smock
|
|---|
| 608 | + * @since xxx
|
|---|
| 609 | + */
|
|---|
| 610 | +class ProtoBufParserTest {
|
|---|
| 611 | + /**
|
|---|
| 612 | + * Check that we are appropriately converting values to the "smallest" type
|
|---|
| 613 | + */
|
|---|
| 614 | + @Test
|
|---|
| 615 | + void testConvertLong() {
|
|---|
| 616 | + // No casting due to auto conversions
|
|---|
| 617 | + assertEquals(Byte.MAX_VALUE, ProtoBufParser.convertLong(Byte.MAX_VALUE));
|
|---|
| 618 | + assertEquals(Byte.MIN_VALUE, ProtoBufParser.convertLong(Byte.MIN_VALUE));
|
|---|
| 619 | + assertEquals(Short.MIN_VALUE, ProtoBufParser.convertLong(Short.MIN_VALUE));
|
|---|
| 620 | + assertEquals(Short.MAX_VALUE, ProtoBufParser.convertLong(Short.MAX_VALUE));
|
|---|
| 621 | + assertEquals(Integer.MAX_VALUE, ProtoBufParser.convertLong(Integer.MAX_VALUE));
|
|---|
| 622 | + assertEquals(Integer.MIN_VALUE, ProtoBufParser.convertLong(Integer.MIN_VALUE));
|
|---|
| 623 | + assertEquals(Long.MIN_VALUE, ProtoBufParser.convertLong(Long.MIN_VALUE));
|
|---|
| 624 | + assertEquals(Long.MAX_VALUE, ProtoBufParser.convertLong(Long.MAX_VALUE));
|
|---|
| 625 | + }
|
|---|
| 626 | +
|
|---|
| 627 | + /**
|
|---|
| 628 | + * Check that zig zags are appropriately encoded.
|
|---|
| 629 | + */
|
|---|
| 630 | + @Test
|
|---|
| 631 | + void testEncodeZigZag() {
|
|---|
| 632 | + assertEquals(0, ProtoBufParser.encodeZigZag(0).byteValue());
|
|---|
| 633 | + assertEquals(1, ProtoBufParser.encodeZigZag(-1).byteValue());
|
|---|
| 634 | + assertEquals(2, ProtoBufParser.encodeZigZag(1).byteValue());
|
|---|
| 635 | + assertEquals(3, ProtoBufParser.encodeZigZag(-2).byteValue());
|
|---|
| 636 | + assertEquals(254, ProtoBufParser.encodeZigZag(Byte.MAX_VALUE).shortValue());
|
|---|
| 637 | + assertEquals(255, ProtoBufParser.encodeZigZag(Byte.MIN_VALUE).shortValue());
|
|---|
| 638 | + assertEquals(65_534, ProtoBufParser.encodeZigZag(Short.MAX_VALUE).intValue());
|
|---|
| 639 | + assertEquals(65_535, ProtoBufParser.encodeZigZag(Short.MIN_VALUE).intValue());
|
|---|
| 640 | + // These integers check a possible boundary condition (the boundary between using the 32/64 bit encoding methods)
|
|---|
| 641 | + assertEquals(4_294_967_292L, ProtoBufParser.encodeZigZag(Integer.MAX_VALUE - 1).longValue());
|
|---|
| 642 | + assertEquals(4_294_967_293L, ProtoBufParser.encodeZigZag(Integer.MIN_VALUE + 1).longValue());
|
|---|
| 643 | + assertEquals(4_294_967_294L, ProtoBufParser.encodeZigZag(Integer.MAX_VALUE).longValue());
|
|---|
| 644 | + assertEquals(4_294_967_295L, ProtoBufParser.encodeZigZag(Integer.MIN_VALUE).longValue());
|
|---|
| 645 | + assertEquals(4_294_967_296L, ProtoBufParser.encodeZigZag(Integer.MAX_VALUE + 1L).longValue());
|
|---|
| 646 | + assertEquals(4_294_967_297L, ProtoBufParser.encodeZigZag(Integer.MIN_VALUE - 1L).longValue());
|
|---|
| 647 | + }
|
|---|
| 648 | +}
|
|---|
| 649 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufRecordTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufRecordTest.java
|
|---|
| 650 | new file mode 100644
|
|---|
| 651 | index 000000000..d0e204c6a
|
|---|
| 652 | --- /dev/null
|
|---|
| 653 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufRecordTest.java
|
|---|
| 654 | @@ -0,0 +1,30 @@
|
|---|
| 655 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 656 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 657 | +
|
|---|
| 658 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 659 | +
|
|---|
| 660 | +
|
|---|
| 661 | +import java.io.IOException;
|
|---|
| 662 | +
|
|---|
| 663 | +import org.junit.jupiter.api.Test;
|
|---|
| 664 | +
|
|---|
| 665 | +/**
|
|---|
| 666 | + * Test class for specific {@link ProtoBufRecord} functionality
|
|---|
| 667 | + */
|
|---|
| 668 | +class ProtoBufRecordTest {
|
|---|
| 669 | + @Test
|
|---|
| 670 | + void testFixed32() throws IOException {
|
|---|
| 671 | + ProtoBufParser parser = new ProtoBufParser(ProtoBufTest.toByteArray(new int[] {0x0d, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 672 | + ProtoBufRecord thirtyTwoBit = new ProtoBufRecord(parser);
|
|---|
| 673 | + assertEquals(WireType.THIRTY_TWO_BIT, thirtyTwoBit.getType());
|
|---|
| 674 | + assertEquals(1f, thirtyTwoBit.asFloat());
|
|---|
| 675 | + }
|
|---|
| 676 | +
|
|---|
| 677 | + @Test
|
|---|
| 678 | + void testUnknown() throws IOException {
|
|---|
| 679 | + ProtoBufParser parser = new ProtoBufParser(ProtoBufTest.toByteArray(new int[] {0x0f, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 680 | + ProtoBufRecord unknown = new ProtoBufRecord(parser);
|
|---|
| 681 | + assertEquals(WireType.UNKNOWN, unknown.getType());
|
|---|
| 682 | + assertEquals(0, unknown.getBytes().length);
|
|---|
| 683 | + }
|
|---|
| 684 | +}
|
|---|
| 685 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 686 | new file mode 100644
|
|---|
| 687 | index 000000000..043481efe
|
|---|
| 688 | --- /dev/null
|
|---|
| 689 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 690 | @@ -0,0 +1,211 @@
|
|---|
| 691 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 692 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 693 | +
|
|---|
| 694 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 695 | +import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|---|
| 696 | +import static org.junit.jupiter.api.Assertions.fail;
|
|---|
| 697 | +
|
|---|
| 698 | +import java.awt.geom.Ellipse2D;
|
|---|
| 699 | +import java.io.ByteArrayInputStream;
|
|---|
| 700 | +import java.io.File;
|
|---|
| 701 | +import java.io.IOException;
|
|---|
| 702 | +import java.io.InputStream;
|
|---|
| 703 | +import java.nio.file.Paths;
|
|---|
| 704 | +import java.text.MessageFormat;
|
|---|
| 705 | +import java.util.ArrayList;
|
|---|
| 706 | +import java.util.Collection;
|
|---|
| 707 | +import java.util.List;
|
|---|
| 708 | +import java.util.stream.Collectors;
|
|---|
| 709 | +
|
|---|
| 710 | +import org.openstreetmap.josm.TestUtils;
|
|---|
| 711 | +import org.openstreetmap.josm.data.coor.LatLon;
|
|---|
| 712 | +import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 713 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Feature;
|
|---|
| 714 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 715 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 716 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorTileSource;
|
|---|
| 717 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 718 | +import org.openstreetmap.josm.data.osm.Node;
|
|---|
| 719 | +import org.openstreetmap.josm.data.osm.Way;
|
|---|
| 720 | +import org.openstreetmap.josm.data.vector.VectorDataSet;
|
|---|
| 721 | +import org.openstreetmap.josm.data.vector.VectorNode;
|
|---|
| 722 | +import org.openstreetmap.josm.data.vector.VectorWay;
|
|---|
| 723 | +import org.openstreetmap.josm.io.Compression;
|
|---|
| 724 | +import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 725 | +
|
|---|
| 726 | +import org.junit.jupiter.api.Test;
|
|---|
| 727 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 728 | +
|
|---|
| 729 | +/**
|
|---|
| 730 | + * Test class for {@link ProtoBufParser} and {@link ProtoBufRecord}
|
|---|
| 731 | + *
|
|---|
| 732 | + * @author Taylor Smock
|
|---|
| 733 | + * @since xxx
|
|---|
| 734 | + */
|
|---|
| 735 | +class ProtoBufTest {
|
|---|
| 736 | + /**
|
|---|
| 737 | + * Convert an int array into a byte array
|
|---|
| 738 | + * @param intArray The int array to convert (NOTE: numbers must be below 255)
|
|---|
| 739 | + * @return A byte array that can be used
|
|---|
| 740 | + */
|
|---|
| 741 | + static byte[] toByteArray(int[] intArray) {
|
|---|
| 742 | + byte[] byteArray = new byte[intArray.length];
|
|---|
| 743 | + for (int i = 0; i < intArray.length; i++) {
|
|---|
| 744 | + if (intArray[i] > Byte.MAX_VALUE - Byte.MIN_VALUE) {
|
|---|
| 745 | + throw new IllegalArgumentException();
|
|---|
| 746 | + }
|
|---|
| 747 | + byteArray[i] = Integer.valueOf(intArray[i]).byteValue();
|
|---|
| 748 | + }
|
|---|
| 749 | + return byteArray;
|
|---|
| 750 | + }
|
|---|
| 751 | +
|
|---|
| 752 | + @RegisterExtension
|
|---|
| 753 | + JOSMTestRules josmTestRules = new JOSMTestRules().preferences();
|
|---|
| 754 | +
|
|---|
| 755 | + private Number bytesToVarInt(int... bytes) {
|
|---|
| 756 | + byte[] byteArray = new byte[bytes.length];
|
|---|
| 757 | + for (int i = 0; i < bytes.length; i++) {
|
|---|
| 758 | + byteArray[i] = (byte) bytes[i];
|
|---|
| 759 | + }
|
|---|
| 760 | + return ProtoBufParser.convertByteArray(byteArray, ProtoBufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 761 | + }
|
|---|
| 762 | +
|
|---|
| 763 | + /**
|
|---|
| 764 | + * Test reading tile from Mapillary ( 14/3248/6258 )
|
|---|
| 765 | + *
|
|---|
| 766 | + * @throws IOException if there is a problem reading the file
|
|---|
| 767 | + */
|
|---|
| 768 | + @Test
|
|---|
| 769 | + void testRead_14_3248_6258() throws IOException {
|
|---|
| 770 | + File vectorTile = Paths.get(TestUtils.getTestDataRoot(), "pbf", "mapillary", "14", "3248", "6258.mvt").toFile();
|
|---|
| 771 | + InputStream inputStream = Compression.getUncompressedFileInputStream(vectorTile);
|
|---|
| 772 | + Collection<ProtoBufRecord> records = new ProtoBufParser(inputStream).allRecords();
|
|---|
| 773 | + assertEquals(2, records.size());
|
|---|
| 774 | + List<Layer> layers = new ArrayList<>();
|
|---|
| 775 | + for (ProtoBufRecord record : records) {
|
|---|
| 776 | + if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 777 | + layers.add(new Layer(record.getBytes()));
|
|---|
| 778 | + } else {
|
|---|
| 779 | + fail(MessageFormat.format("Invalid field {0}", record.getField()));
|
|---|
| 780 | + }
|
|---|
| 781 | + }
|
|---|
| 782 | + Layer mapillarySequences = layers.get(0);
|
|---|
| 783 | + Layer mapillaryPictures = layers.get(1);
|
|---|
| 784 | + assertEquals("mapillary-sequences", mapillarySequences.getName());
|
|---|
| 785 | + assertEquals("mapillary-images", mapillaryPictures.getName());
|
|---|
| 786 | + assertEquals(2048, mapillarySequences.getExtent());
|
|---|
| 787 | + assertEquals(2048, mapillaryPictures.getExtent());
|
|---|
| 788 | +
|
|---|
| 789 | + assertEquals(1,
|
|---|
| 790 | + mapillarySequences.getFeatures().stream().filter(feature -> feature.getId() == 233760500).count());
|
|---|
| 791 | + Feature testSequence = mapillarySequences.getFeatures().stream().filter(feature -> feature.getId() == 233760500)
|
|---|
| 792 | + .findAny().orElse(null);
|
|---|
| 793 | + assertEquals("dpudn262yz6aitu33zh7bl", testSequence.getTags().get("key"));
|
|---|
| 794 | + assertEquals("clnaw3kpokIAe_CsN5Qmiw", testSequence.getTags().get("ikey"));
|
|---|
| 795 | + assertEquals("B1iNjH4Ohn25cRAGPhetfw", testSequence.getTags().get("userkey"));
|
|---|
| 796 | + assertEquals(Long.valueOf(1557535457401L), Long.valueOf(testSequence.getTags().get("captured_at")));
|
|---|
| 797 | + assertEquals(0, Integer.parseInt(testSequence.getTags().get("pano")));
|
|---|
| 798 | + }
|
|---|
| 799 | +
|
|---|
| 800 | + @Test
|
|---|
| 801 | + void testRead_17_26028_50060() throws IOException {
|
|---|
| 802 | + File vectorTile = Paths.get(TestUtils.getTestDataRoot(), "pbf", "openinframap", "17", "26028", "50060.pbf")
|
|---|
| 803 | + .toFile();
|
|---|
| 804 | + InputStream inputStream = Compression.getUncompressedFileInputStream(vectorTile);
|
|---|
| 805 | + Collection<ProtoBufRecord> records = new ProtoBufParser(inputStream).allRecords();
|
|---|
| 806 | + List<Layer> layers = new ArrayList<>();
|
|---|
| 807 | + for (ProtoBufRecord record : records) {
|
|---|
| 808 | + if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 809 | + layers.add(new Layer(record.getBytes()));
|
|---|
| 810 | + } else {
|
|---|
| 811 | + fail(MessageFormat.format("Invalid field {0}", record.getField()));
|
|---|
| 812 | + }
|
|---|
| 813 | + }
|
|---|
| 814 | + assertEquals(19, layers.size());
|
|---|
| 815 | + List<Layer> dataLayers = layers.stream().filter(layer -> !layer.getFeatures().isEmpty())
|
|---|
| 816 | + .collect(Collectors.toList());
|
|---|
| 817 | + // power_plant, power_plant_point, power_generator, power_heatmap_solar, and power_generator_area
|
|---|
| 818 | + assertEquals(5, dataLayers.size());
|
|---|
| 819 | +
|
|---|
| 820 | + // power_generator_area was rendered incorrectly
|
|---|
| 821 | + final Layer powerGeneratorArea = dataLayers.stream()
|
|---|
| 822 | + .filter(layer -> "power_generator_area".equals(layer.getName())).findAny().orElse(null);
|
|---|
| 823 | + assertNotNull(powerGeneratorArea);
|
|---|
| 824 | + final int extent = powerGeneratorArea.getExtent();
|
|---|
| 825 | + // 17/26028/50060 bounds
|
|---|
| 826 | + VectorDataSet vectorDataSet = new VectorDataSet();
|
|---|
| 827 | + MVTTile vectorTile1 = new MVTTile(new MapboxVectorTileSource(new ImageryInfo("Test info", "example.org")),
|
|---|
| 828 | + 26028, 50060, 17);
|
|---|
| 829 | + vectorTile1.loadImage(Compression.getUncompressedFileInputStream(vectorTile));
|
|---|
| 830 | + vectorDataSet.addTileData(vectorTile1);
|
|---|
| 831 | + vectorDataSet.setZoom(17);
|
|---|
| 832 | + final Way one = new Way();
|
|---|
| 833 | + one.addNode(new Node(new LatLon(39.0687509, -108.5100816)));
|
|---|
| 834 | + one.addNode(new Node(new LatLon(39.0687509, -108.5095751)));
|
|---|
| 835 | + one.addNode(new Node(new LatLon(39.0687169, -108.5095751)));
|
|---|
| 836 | + one.addNode(new Node(new LatLon(39.0687169, -108.5100816)));
|
|---|
| 837 | + one.addNode(one.getNode(0));
|
|---|
| 838 | + one.setOsmId(666293899, 2);
|
|---|
| 839 | + final BBox searchBBox = one.getBBox();
|
|---|
| 840 | + searchBBox.addPrimitive(one, 0.00001);
|
|---|
| 841 | + final Collection<VectorNode> searchedNodes = vectorDataSet.searchNodes(searchBBox);
|
|---|
| 842 | + final Collection<VectorWay> searchedWays = vectorDataSet.searchWays(searchBBox);
|
|---|
| 843 | + assertEquals(4, searchedNodes.size());
|
|---|
| 844 | + }
|
|---|
| 845 | +
|
|---|
| 846 | + @Test
|
|---|
| 847 | + void testReadVarInt() {
|
|---|
| 848 | + assertEquals(ProtoBufParser.convertLong(0), bytesToVarInt(0x0));
|
|---|
| 849 | + assertEquals(ProtoBufParser.convertLong(1), bytesToVarInt(0x1));
|
|---|
| 850 | + assertEquals(ProtoBufParser.convertLong(127), bytesToVarInt(0x7f));
|
|---|
| 851 | + // This should b 0xff 0xff 0xff 0xff 0x07, but we drop the leading bit when reading to a byte array
|
|---|
| 852 | + Number actual = bytesToVarInt(0x7f, 0x7f, 0x7f, 0x7f, 0x07);
|
|---|
| 853 | + assertEquals(ProtoBufParser.convertLong(Integer.MAX_VALUE), actual,
|
|---|
| 854 | + MessageFormat.format("Expected {0} but got {1}", Integer.toBinaryString(Integer.MAX_VALUE),
|
|---|
| 855 | + Long.toBinaryString(actual.longValue())));
|
|---|
| 856 | + }
|
|---|
| 857 | +
|
|---|
| 858 | + /**
|
|---|
| 859 | + * Test simple message.
|
|---|
| 860 | + * Check that a simple message is readable
|
|---|
| 861 | + *
|
|---|
| 862 | + * @throws IOException - if an IO error occurs
|
|---|
| 863 | + */
|
|---|
| 864 | + @Test
|
|---|
| 865 | + void testSimpleMessage() throws IOException {
|
|---|
| 866 | + ProtoBufParser parser = new ProtoBufParser(new byte[] {(byte) 0x08, (byte) 0x96, (byte) 0x01});
|
|---|
| 867 | + ProtoBufRecord record = new ProtoBufRecord(parser);
|
|---|
| 868 | + assertEquals(WireType.VARINT, record.getType());
|
|---|
| 869 | + assertEquals(150, record.asUnsignedVarInt().intValue());
|
|---|
| 870 | + }
|
|---|
| 871 | +
|
|---|
| 872 | + @Test
|
|---|
| 873 | + void testSingletonMultiPoint() throws IOException {
|
|---|
| 874 | + Collection<ProtoBufRecord> records = new ProtoBufParser(new ByteArrayInputStream(toByteArray(
|
|---|
| 875 | + new int[] {0x1a, 0x2c, 0x78, 0x02, 0x0a, 0x03, 0x74, 0x6d, 0x70, 0x28, 0x80, 0x20, 0x1a, 0x04, 0x6e,
|
|---|
| 876 | + 0x61, 0x6d, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65,
|
|---|
| 877 | + 0x12, 0x0d, 0x18, 0x01, 0x12, 0x02, 0x00, 0x00, 0x22, 0x05, 0x09, 0xe0, 0x3e, 0x84, 0x27})))
|
|---|
| 878 | + .allRecords();
|
|---|
| 879 | + List<Layer> layers = new ArrayList<>();
|
|---|
| 880 | + for (ProtoBufRecord record : records) {
|
|---|
| 881 | + if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 882 | + layers.add(new Layer(record.getBytes()));
|
|---|
| 883 | + } else {
|
|---|
| 884 | + fail(MessageFormat.format("Invalid field {0}", record.getField()));
|
|---|
| 885 | + }
|
|---|
| 886 | + }
|
|---|
| 887 | + assertEquals(1, layers.size());
|
|---|
| 888 | + assertEquals(1, layers.get(0).getGeometry().size());
|
|---|
| 889 | + Ellipse2D shape = (Ellipse2D) layers.get(0).getGeometry().iterator().next().getShapes().iterator().next();
|
|---|
| 890 | + assertEquals(4016, shape.getCenterX());
|
|---|
| 891 | + assertEquals(2498, shape.getCenterY());
|
|---|
| 892 | + }
|
|---|
| 893 | +
|
|---|
| 894 | + @Test
|
|---|
| 895 | + void testZigZag() {
|
|---|
| 896 | + assertEquals(0, ProtoBufParser.decodeZigZag(0).intValue());
|
|---|
| 897 | + assertEquals(-1, ProtoBufParser.decodeZigZag(1).intValue());
|
|---|
| 898 | + assertEquals(1, ProtoBufParser.decodeZigZag(2).intValue());
|
|---|
| 899 | + assertEquals(-2, ProtoBufParser.decodeZigZag(3).intValue());
|
|---|
| 900 | + }
|
|---|
| 901 | +}
|
|---|
| 902 | --
|
|---|
| 903 | GitLab
|
|---|
| 904 |
|
|---|
| 905 |
|
|---|
| 906 | From f38f09e1036905ea7e7499c66b5a9fb691cd38c8 Mon Sep 17 00:00:00 2001
|
|---|
| 907 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 908 | Date: Thu, 8 Apr 2021 14:24:54 -0600
|
|---|
| 909 | Subject: [PATCH 02/50] Initial Mapbox Vector Tile implementation
|
|---|
| 910 |
|
|---|
| 911 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 912 | ---
|
|---|
| 913 | .../data/imagery/vectortile/VectorTile.java | 25 ++
|
|---|
| 914 | .../imagery/vectortile/mapbox/Command.java | 48 ++++
|
|---|
| 915 | .../vectortile/mapbox/CommandInteger.java | 62 +++++
|
|---|
| 916 | .../imagery/vectortile/mapbox/Feature.java | 175 +++++++++++++
|
|---|
| 917 | .../imagery/vectortile/mapbox/Geometry.java | 102 ++++++++
|
|---|
| 918 | .../vectortile/mapbox/GeometryTypes.java | 31 +++
|
|---|
| 919 | .../InvalidMapboxVectorTileException.java | 25 ++
|
|---|
| 920 | .../data/imagery/vectortile/mapbox/Layer.java | 245 ++++++++++++++++++
|
|---|
| 921 | .../imagery/vectortile/mapbox/MVTFile.java | 34 +++
|
|---|
| 922 | .../imagery/vectortile/mapbox/MVTTile.java | 119 +++++++++
|
|---|
| 923 | .../mapbox/MapBoxVectorCachedTileLoader.java | 79 ++++++
|
|---|
| 924 | .../MapBoxVectorCachedTileLoaderJob.java | 26 ++
|
|---|
| 925 | .../mapbox/MapboxVectorTileSource.java | 92 +++++++
|
|---|
| 926 | test/data/mapillary.json | 111 ++++++++
|
|---|
| 927 | .../vectortile/mapbox/FeatureTest.java | 122 +++++++++
|
|---|
| 928 | .../vectortile/mapbox/GeometryTest.java | 169 ++++++++++++
|
|---|
| 929 | .../vectortile/mapbox/GeometryTypesTest.java | 47 ++++
|
|---|
| 930 | .../imagery/vectortile/mapbox/LayerTest.java | 135 ++++++++++
|
|---|
| 931 | .../vectortile/mapbox/MVTTileTest.java | 82 ++++++
|
|---|
| 932 | .../mapbox/MapboxVectorTileSourceTest.java | 77 ++++++
|
|---|
| 933 | 20 files changed, 1806 insertions(+)
|
|---|
| 934 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/VectorTile.java
|
|---|
| 935 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Command.java
|
|---|
| 936 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/CommandInteger.java
|
|---|
| 937 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
|
|---|
| 938 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Geometry.java
|
|---|
| 939 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypes.java
|
|---|
| 940 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java
|
|---|
| 941 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 942 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java
|
|---|
| 943 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 944 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoader.java
|
|---|
| 945 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoaderJob.java
|
|---|
| 946 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 947 | create mode 100644 test/data/mapillary.json
|
|---|
| 948 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java
|
|---|
| 949 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTest.java
|
|---|
| 950 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypesTest.java
|
|---|
| 951 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 952 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 953 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java
|
|---|
| 954 |
|
|---|
| 955 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/VectorTile.java b/src/org/openstreetmap/josm/data/imagery/vectortile/VectorTile.java
|
|---|
| 956 | new file mode 100644
|
|---|
| 957 | index 000000000..692f3ea8c
|
|---|
| 958 | --- /dev/null
|
|---|
| 959 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/VectorTile.java
|
|---|
| 960 | @@ -0,0 +1,25 @@
|
|---|
| 961 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 962 | +package org.openstreetmap.josm.data.imagery.vectortile;
|
|---|
| 963 | +
|
|---|
| 964 | +import java.util.Collection;
|
|---|
| 965 | +
|
|---|
| 966 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 967 | +
|
|---|
| 968 | +/**
|
|---|
| 969 | + * An interface that is used to draw vector tiles, instead of using images
|
|---|
| 970 | + * @author Taylor Smock
|
|---|
| 971 | + * @since xxx
|
|---|
| 972 | + */
|
|---|
| 973 | +public interface VectorTile {
|
|---|
| 974 | + /**
|
|---|
| 975 | + * Get the layers for this vector tile
|
|---|
| 976 | + * @return A collection of layers
|
|---|
| 977 | + */
|
|---|
| 978 | + Collection<Layer> getLayers();
|
|---|
| 979 | +
|
|---|
| 980 | + /**
|
|---|
| 981 | + * Get the extent of the tile (in pixels)
|
|---|
| 982 | + * @return The tile extent (pixels)
|
|---|
| 983 | + */
|
|---|
| 984 | + int getExtent();
|
|---|
| 985 | +}
|
|---|
| 986 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Command.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Command.java
|
|---|
| 987 | new file mode 100644
|
|---|
| 988 | index 000000000..05ffcf945
|
|---|
| 989 | --- /dev/null
|
|---|
| 990 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Command.java
|
|---|
| 991 | @@ -0,0 +1,48 @@
|
|---|
| 992 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 993 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 994 | +
|
|---|
| 995 | +/**
|
|---|
| 996 | + * Command integers for Mapbox Vector Tiles
|
|---|
| 997 | + * @author Taylor Smock
|
|---|
| 998 | + * @since xxx
|
|---|
| 999 | + */
|
|---|
| 1000 | +public enum Command {
|
|---|
| 1001 | + /**
|
|---|
| 1002 | + * For {@link GeometryTypes#POINT}, each {@link #MoveTo} is a new point.
|
|---|
| 1003 | + * For {@link GeometryTypes#LINESTRING} and {@link GeometryTypes#POLYGON}, each {@link #MoveTo} is a new geometry of the same type.
|
|---|
| 1004 | + */
|
|---|
| 1005 | + MoveTo((byte) 1, (byte) 2),
|
|---|
| 1006 | + /**
|
|---|
| 1007 | + * While not explicitly prohibited for {@link GeometryTypes#POINT}, it should be ignored.
|
|---|
| 1008 | + * For {@link GeometryTypes#LINESTRING} and {@link GeometryTypes#POLYGON}, each {@link #LineTo} extends that geometry.
|
|---|
| 1009 | + */
|
|---|
| 1010 | + LineTo((byte) 2, (byte) 2),
|
|---|
| 1011 | + /**
|
|---|
| 1012 | + * This is only explicitly valid for {@link GeometryTypes#POLYGON}. It closes the {@link GeometryTypes#POLYGON}.
|
|---|
| 1013 | + */
|
|---|
| 1014 | + ClosePath((byte) 7, (byte) 0);
|
|---|
| 1015 | +
|
|---|
| 1016 | + private final byte id;
|
|---|
| 1017 | + private final byte parameters;
|
|---|
| 1018 | +
|
|---|
| 1019 | + Command(byte id, byte parameters) {
|
|---|
| 1020 | + this.id = id;
|
|---|
| 1021 | + this.parameters = parameters;
|
|---|
| 1022 | + }
|
|---|
| 1023 | +
|
|---|
| 1024 | + /**
|
|---|
| 1025 | + * Get the command id
|
|---|
| 1026 | + * @return The id
|
|---|
| 1027 | + */
|
|---|
| 1028 | + public byte getId() {
|
|---|
| 1029 | + return this.id;
|
|---|
| 1030 | + }
|
|---|
| 1031 | +
|
|---|
| 1032 | + /**
|
|---|
| 1033 | + * Get the number of parameters
|
|---|
| 1034 | + * @return The number of parameters
|
|---|
| 1035 | + */
|
|---|
| 1036 | + public byte getParameterNumber() {
|
|---|
| 1037 | + return this.parameters;
|
|---|
| 1038 | + }
|
|---|
| 1039 | +}
|
|---|
| 1040 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/CommandInteger.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/CommandInteger.java
|
|---|
| 1041 | new file mode 100644
|
|---|
| 1042 | index 000000000..5213bf0e8
|
|---|
| 1043 | --- /dev/null
|
|---|
| 1044 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/CommandInteger.java
|
|---|
| 1045 | @@ -0,0 +1,62 @@
|
|---|
| 1046 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1047 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1048 | +
|
|---|
| 1049 | +import java.util.stream.Stream;
|
|---|
| 1050 | +
|
|---|
| 1051 | +/**
|
|---|
| 1052 | + * An indicator for a command to be executed
|
|---|
| 1053 | + * @author Taylor Smock
|
|---|
| 1054 | + * @since xxx
|
|---|
| 1055 | + */
|
|---|
| 1056 | +public class CommandInteger {
|
|---|
| 1057 | + private final Command type;
|
|---|
| 1058 | + private final short[] parameters;
|
|---|
| 1059 | + private int added;
|
|---|
| 1060 | +
|
|---|
| 1061 | + /**
|
|---|
| 1062 | + * Create a new command
|
|---|
| 1063 | + * @param command the command (treated as an unsigned int)
|
|---|
| 1064 | + */
|
|---|
| 1065 | + public CommandInteger(final int command) {
|
|---|
| 1066 | + // Technically, the int is unsigned, but it is easier to work with the long
|
|---|
| 1067 | + final long unsigned = Integer.toUnsignedLong(command);
|
|---|
| 1068 | + this.type = Stream.of(Command.values()).filter(e -> e.getId() == (unsigned & 0x7)).findAny()
|
|---|
| 1069 | + .orElseThrow(InvalidMapboxVectorTileException::new);
|
|---|
| 1070 | + // This is safe, since we are shifting right 3 when we converted an int to a long (for unsigned).
|
|---|
| 1071 | + // So we <i>cannot</i> lose anything.
|
|---|
| 1072 | + final int operationsInt = (int) (unsigned >> 3);
|
|---|
| 1073 | + this.parameters = new short[operationsInt * this.type.getParameterNumber()];
|
|---|
| 1074 | + }
|
|---|
| 1075 | +
|
|---|
| 1076 | + /**
|
|---|
| 1077 | + * Add a parameter
|
|---|
| 1078 | + * @param parameterInteger The parameter to add (converted to {@link short}).
|
|---|
| 1079 | + */
|
|---|
| 1080 | + public void addParameter(Number parameterInteger) {
|
|---|
| 1081 | + this.parameters[added++] = parameterInteger.shortValue();
|
|---|
| 1082 | + }
|
|---|
| 1083 | +
|
|---|
| 1084 | + /**
|
|---|
| 1085 | + * Get the operations for the command
|
|---|
| 1086 | + * @return The operations
|
|---|
| 1087 | + */
|
|---|
| 1088 | + public short[] getOperations() {
|
|---|
| 1089 | + return this.parameters;
|
|---|
| 1090 | + }
|
|---|
| 1091 | +
|
|---|
| 1092 | + /**
|
|---|
| 1093 | + * Get the command type
|
|---|
| 1094 | + * @return the command type
|
|---|
| 1095 | + */
|
|---|
| 1096 | + public Command getType() {
|
|---|
| 1097 | + return this.type;
|
|---|
| 1098 | + }
|
|---|
| 1099 | +
|
|---|
| 1100 | + /**
|
|---|
| 1101 | + * Get the expected parameter length
|
|---|
| 1102 | + * @return The expected parameter size
|
|---|
| 1103 | + */
|
|---|
| 1104 | + public boolean hasAllExpectedParameters() {
|
|---|
| 1105 | + return this.added >= this.parameters.length;
|
|---|
| 1106 | + }
|
|---|
| 1107 | +}
|
|---|
| 1108 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
|
|---|
| 1109 | new file mode 100644
|
|---|
| 1110 | index 000000000..df194cc00
|
|---|
| 1111 | --- /dev/null
|
|---|
| 1112 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
|
|---|
| 1113 | @@ -0,0 +1,175 @@
|
|---|
| 1114 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1115 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1116 | +
|
|---|
| 1117 | +import java.io.IOException;
|
|---|
| 1118 | +import java.text.NumberFormat;
|
|---|
| 1119 | +import java.util.ArrayList;
|
|---|
| 1120 | +import java.util.List;
|
|---|
| 1121 | +
|
|---|
| 1122 | +import org.openstreetmap.josm.data.osm.TagMap;
|
|---|
| 1123 | +import org.openstreetmap.josm.data.protobuf.ProtoBufPacked;
|
|---|
| 1124 | +import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 1125 | +import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 1126 | +import org.openstreetmap.josm.tools.Utils;
|
|---|
| 1127 | +
|
|---|
| 1128 | +/**
|
|---|
| 1129 | + * A Feature for a {@link Layer}
|
|---|
| 1130 | + *
|
|---|
| 1131 | + * @author Taylor Smock
|
|---|
| 1132 | + * @since xxx
|
|---|
| 1133 | + */
|
|---|
| 1134 | +public class Feature {
|
|---|
| 1135 | + private static final byte ID_FIELD = 1;
|
|---|
| 1136 | + private static final byte TAG_FIELD = 2;
|
|---|
| 1137 | + private static final byte GEOMETRY_TYPE_FIELD = 3;
|
|---|
| 1138 | + private static final byte GEOMETRY_FIELD = 4;
|
|---|
| 1139 | + /**
|
|---|
| 1140 | + * The geometry of the feature. Required.
|
|---|
| 1141 | + */
|
|---|
| 1142 | + private final List<CommandInteger> geometry = new ArrayList<>();
|
|---|
| 1143 | +
|
|---|
| 1144 | + /**
|
|---|
| 1145 | + * The geometry type of the feature. Required.
|
|---|
| 1146 | + */
|
|---|
| 1147 | + private final GeometryTypes geometryType;
|
|---|
| 1148 | + /**
|
|---|
| 1149 | + * The id of the feature. Optional.
|
|---|
| 1150 | + */
|
|---|
| 1151 | + // Technically, uint64
|
|---|
| 1152 | + private final long id;
|
|---|
| 1153 | + /**
|
|---|
| 1154 | + * The tags of the feature. Optional.
|
|---|
| 1155 | + */
|
|---|
| 1156 | + private TagMap tags;
|
|---|
| 1157 | + private Geometry geometryObject;
|
|---|
| 1158 | +
|
|---|
| 1159 | + /**
|
|---|
| 1160 | + * Create a new Feature
|
|---|
| 1161 | + *
|
|---|
| 1162 | + * @param layer The layer the feature is part of (required for tags)
|
|---|
| 1163 | + * @param record The record to create the feature from
|
|---|
| 1164 | + * @throws IOException - if an IO error occurs
|
|---|
| 1165 | + */
|
|---|
| 1166 | + public Feature(Layer layer, ProtoBufRecord record) throws IOException {
|
|---|
| 1167 | + long tId = 0;
|
|---|
| 1168 | + GeometryTypes geometryTypeTemp = GeometryTypes.UNKNOWN;
|
|---|
| 1169 | + String key = null;
|
|---|
| 1170 | + try (ProtoBufParser parser = new ProtoBufParser(record.getBytes())) {
|
|---|
| 1171 | + while (parser.hasNext()) {
|
|---|
| 1172 | + try (ProtoBufRecord next = new ProtoBufRecord(parser)) {
|
|---|
| 1173 | + if (next.getField() == TAG_FIELD) {
|
|---|
| 1174 | + if (tags == null) {
|
|---|
| 1175 | + tags = new TagMap();
|
|---|
| 1176 | + }
|
|---|
| 1177 | + // This is packed in v1 and v2
|
|---|
| 1178 | + ProtoBufPacked packed = new ProtoBufPacked(next.getBytes());
|
|---|
| 1179 | + for (Number number : packed.getArray()) {
|
|---|
| 1180 | + key = parseTagValue(key, layer, number);
|
|---|
| 1181 | + }
|
|---|
| 1182 | + } else if (next.getField() == GEOMETRY_FIELD) {
|
|---|
| 1183 | + // This is packed in v1 and v2
|
|---|
| 1184 | + ProtoBufPacked packed = new ProtoBufPacked(next.getBytes());
|
|---|
| 1185 | + CommandInteger currentCommand = null;
|
|---|
| 1186 | + for (Number number : packed.getArray()) {
|
|---|
| 1187 | + if (currentCommand != null && currentCommand.hasAllExpectedParameters()) {
|
|---|
| 1188 | + currentCommand = null;
|
|---|
| 1189 | + }
|
|---|
| 1190 | + if (currentCommand == null) {
|
|---|
| 1191 | + currentCommand = new CommandInteger(number.intValue());
|
|---|
| 1192 | + this.geometry.add(currentCommand);
|
|---|
| 1193 | + } else {
|
|---|
| 1194 | + currentCommand.addParameter(ProtoBufParser.decodeZigZag(number));
|
|---|
| 1195 | + }
|
|---|
| 1196 | + }
|
|---|
| 1197 | + // TODO fallback to non-packed
|
|---|
| 1198 | + } else if (next.getField() == GEOMETRY_TYPE_FIELD) {
|
|---|
| 1199 | + geometryTypeTemp = GeometryTypes.values()[next.asUnsignedVarInt().intValue()];
|
|---|
| 1200 | + } else if (next.getField() == ID_FIELD) {
|
|---|
| 1201 | + tId = next.asUnsignedVarInt().longValue();
|
|---|
| 1202 | + }
|
|---|
| 1203 | + }
|
|---|
| 1204 | + }
|
|---|
| 1205 | + }
|
|---|
| 1206 | + this.id = tId;
|
|---|
| 1207 | + this.geometryType = geometryTypeTemp;
|
|---|
| 1208 | + record.close();
|
|---|
| 1209 | + }
|
|---|
| 1210 | +
|
|---|
| 1211 | + /**
|
|---|
| 1212 | + * Parse a tag value
|
|---|
| 1213 | + *
|
|---|
| 1214 | + * @param key The current key (or {@code null}, if {@code null}, the returned value will be the new key)
|
|---|
| 1215 | + * @param layer The layer with key/value information
|
|---|
| 1216 | + * @param number The number to get the value from
|
|---|
| 1217 | + * @return The new key (if {@code null}, then a value was parsed and added to tags)
|
|---|
| 1218 | + */
|
|---|
| 1219 | + private String parseTagValue(String key, Layer layer, Number number) {
|
|---|
| 1220 | + if (key == null) {
|
|---|
| 1221 | + key = layer.getKey(number.intValue());
|
|---|
| 1222 | + } else {
|
|---|
| 1223 | + Object value = layer.getValue(number.intValue());
|
|---|
| 1224 | + if (value instanceof Double || value instanceof Float) {
|
|---|
| 1225 | + // reset grouping if the instance is a singleton
|
|---|
| 1226 | + final NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
|---|
| 1227 | + final boolean grouping = numberFormat.isGroupingUsed();
|
|---|
| 1228 | + try {
|
|---|
| 1229 | + numberFormat.setGroupingUsed(false);
|
|---|
| 1230 | + this.tags.put(key, numberFormat.format(value));
|
|---|
| 1231 | + } finally {
|
|---|
| 1232 | + numberFormat.setGroupingUsed(grouping);
|
|---|
| 1233 | + }
|
|---|
| 1234 | + } else {
|
|---|
| 1235 | + this.tags.put(key, Utils.intern(value.toString()));
|
|---|
| 1236 | + }
|
|---|
| 1237 | + key = null;
|
|---|
| 1238 | + }
|
|---|
| 1239 | + return key;
|
|---|
| 1240 | + }
|
|---|
| 1241 | +
|
|---|
| 1242 | + /**
|
|---|
| 1243 | + * Get the geometry instructions
|
|---|
| 1244 | + *
|
|---|
| 1245 | + * @return The geometry
|
|---|
| 1246 | + */
|
|---|
| 1247 | + public List<CommandInteger> getGeometry() {
|
|---|
| 1248 | + return this.geometry;
|
|---|
| 1249 | + }
|
|---|
| 1250 | +
|
|---|
| 1251 | + /**
|
|---|
| 1252 | + * Get the geometry type
|
|---|
| 1253 | + *
|
|---|
| 1254 | + * @return The {@link GeometryTypes}
|
|---|
| 1255 | + */
|
|---|
| 1256 | + public GeometryTypes getGeometryType() {
|
|---|
| 1257 | + return this.geometryType;
|
|---|
| 1258 | + }
|
|---|
| 1259 | +
|
|---|
| 1260 | + /**
|
|---|
| 1261 | + * Get the id of the object
|
|---|
| 1262 | + *
|
|---|
| 1263 | + * @return The unique id in the layer, or 0.
|
|---|
| 1264 | + */
|
|---|
| 1265 | + public long getId() {
|
|---|
| 1266 | + return this.id;
|
|---|
| 1267 | + }
|
|---|
| 1268 | +
|
|---|
| 1269 | + /**
|
|---|
| 1270 | + * Get the tags
|
|---|
| 1271 | + *
|
|---|
| 1272 | + * @return A tag map
|
|---|
| 1273 | + */
|
|---|
| 1274 | + public TagMap getTags() {
|
|---|
| 1275 | + return this.tags;
|
|---|
| 1276 | + }
|
|---|
| 1277 | +
|
|---|
| 1278 | + /**
|
|---|
| 1279 | + * Get the an object with shapes for the geometry
|
|---|
| 1280 | + * @return An object with usable geometry information
|
|---|
| 1281 | + */
|
|---|
| 1282 | + public Geometry getGeometryObject() {
|
|---|
| 1283 | + if (this.geometryObject == null) {
|
|---|
| 1284 | + this.geometryObject = new Geometry(this.getGeometryType(), this.getGeometry());
|
|---|
| 1285 | + }
|
|---|
| 1286 | + return this.geometryObject;
|
|---|
| 1287 | + }
|
|---|
| 1288 | +}
|
|---|
| 1289 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Geometry.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Geometry.java
|
|---|
| 1290 | new file mode 100644
|
|---|
| 1291 | index 000000000..c612c7e83
|
|---|
| 1292 | --- /dev/null
|
|---|
| 1293 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Geometry.java
|
|---|
| 1294 | @@ -0,0 +1,102 @@
|
|---|
| 1295 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1296 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1297 | +
|
|---|
| 1298 | +import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 1299 | +
|
|---|
| 1300 | +import java.awt.Shape;
|
|---|
| 1301 | +import java.awt.geom.Area;
|
|---|
| 1302 | +import java.awt.geom.Ellipse2D;
|
|---|
| 1303 | +import java.awt.geom.Path2D;
|
|---|
| 1304 | +import java.util.ArrayList;
|
|---|
| 1305 | +import java.util.Collection;
|
|---|
| 1306 | +import java.util.Collections;
|
|---|
| 1307 | +import java.util.List;
|
|---|
| 1308 | +
|
|---|
| 1309 | +/**
|
|---|
| 1310 | + * A class to generate geometry for a vector tile
|
|---|
| 1311 | + * @author Taylor Smock
|
|---|
| 1312 | + * @since xxx
|
|---|
| 1313 | + */
|
|---|
| 1314 | +public class Geometry {
|
|---|
| 1315 | + final Collection<Shape> shapes = new ArrayList<>();
|
|---|
| 1316 | +
|
|---|
| 1317 | + /**
|
|---|
| 1318 | + * Create a {@link Geometry} for a {@link Feature}
|
|---|
| 1319 | + * @param geometryType The type of geometry
|
|---|
| 1320 | + * @param commands The commands used to create the geometry
|
|---|
| 1321 | + */
|
|---|
| 1322 | + public Geometry(GeometryTypes geometryType, List<CommandInteger> commands) {
|
|---|
| 1323 | + if (geometryType == GeometryTypes.POINT) {
|
|---|
| 1324 | + for (CommandInteger command : commands) {
|
|---|
| 1325 | + final short[] operations = command.getOperations();
|
|---|
| 1326 | + // Each MoveTo command is a new point
|
|---|
| 1327 | + if (command.getType() == Command.MoveTo && operations.length % 2 == 0 && operations.length > 0) {
|
|---|
| 1328 | + for (int i = 0; i < operations.length / 2; i++) {
|
|---|
| 1329 | + // Just using Ellipse2D since it extends Shape
|
|---|
| 1330 | + shapes.add(new Ellipse2D.Float(operations[2 * i],
|
|---|
| 1331 | + operations[2 * i + 1], 0, 0));
|
|---|
| 1332 | + }
|
|---|
| 1333 | + } else {
|
|---|
| 1334 | + throw new IllegalArgumentException(tr("{0} with {1} arguments is not understood", geometryType, operations.length));
|
|---|
| 1335 | + }
|
|---|
| 1336 | + }
|
|---|
| 1337 | + } else if (geometryType == GeometryTypes.LINESTRING || geometryType == GeometryTypes.POLYGON) {
|
|---|
| 1338 | + Path2D.Float line = null;
|
|---|
| 1339 | + Area area = null;
|
|---|
| 1340 | + // MVT uses delta encoding. Each feature starts at (0, 0).
|
|---|
| 1341 | + double x = 0;
|
|---|
| 1342 | + double y = 0;
|
|---|
| 1343 | + // Area is used to determine the inner/outer of a polygon
|
|---|
| 1344 | + double areaAreaSq = 0;
|
|---|
| 1345 | + for (CommandInteger command : commands) {
|
|---|
| 1346 | + final short[] operations = command.getOperations();
|
|---|
| 1347 | + // Technically, there is no reason why there can be multiple MoveTo operations in one command, but that is undefined behavior
|
|---|
| 1348 | + if (command.getType() == Command.MoveTo && operations.length == 2) {
|
|---|
| 1349 | + areaAreaSq = 0;
|
|---|
| 1350 | + x += operations[0];
|
|---|
| 1351 | + y += operations[1];
|
|---|
| 1352 | + line = new Path2D.Float();
|
|---|
| 1353 | + line.moveTo(x, y);
|
|---|
| 1354 | + shapes.add(line);
|
|---|
| 1355 | + } else if (command.getType() == Command.LineTo && operations.length % 2 == 0 && line != null) {
|
|---|
| 1356 | + for (int i = 0; i < operations.length / 2; i++) {
|
|---|
| 1357 | + final double lx = x;
|
|---|
| 1358 | + final double ly = y;
|
|---|
| 1359 | + x += operations[2 * i];
|
|---|
| 1360 | + y += operations[2 * i + 1];
|
|---|
| 1361 | + areaAreaSq += lx * y - x * ly;
|
|---|
| 1362 | + line.lineTo(x, y);
|
|---|
| 1363 | + }
|
|---|
| 1364 | + // ClosePath should only be used with Polygon geometry
|
|---|
| 1365 | + } else if (geometryType == GeometryTypes.POLYGON && command.getType() == Command.ClosePath && line != null) {
|
|---|
| 1366 | + shapes.remove(line);
|
|---|
| 1367 | + // new Area() closes the line if it isn't already closed
|
|---|
| 1368 | + if (area == null) {
|
|---|
| 1369 | + area = new Area();
|
|---|
| 1370 | + shapes.add(area);
|
|---|
| 1371 | + }
|
|---|
| 1372 | +
|
|---|
| 1373 | + Area nArea = new Area(line);
|
|---|
| 1374 | + // SonarLint thinks that this is never > 0. It can be.
|
|---|
| 1375 | + if (areaAreaSq > 0) {
|
|---|
| 1376 | + area.add(nArea);
|
|---|
| 1377 | + } else if (areaAreaSq < 0) {
|
|---|
| 1378 | + area.exclusiveOr(nArea);
|
|---|
| 1379 | + } else {
|
|---|
| 1380 | + throw new IllegalArgumentException(tr("{0} cannot have zero area", geometryType));
|
|---|
| 1381 | + }
|
|---|
| 1382 | + } else {
|
|---|
| 1383 | + throw new IllegalArgumentException(tr("{0} with {1} arguments is not understood", geometryType, operations.length));
|
|---|
| 1384 | + }
|
|---|
| 1385 | + }
|
|---|
| 1386 | + }
|
|---|
| 1387 | + }
|
|---|
| 1388 | +
|
|---|
| 1389 | + /**
|
|---|
| 1390 | + * Get the shapes to draw this geometry with
|
|---|
| 1391 | + * @return A collection of shapes
|
|---|
| 1392 | + */
|
|---|
| 1393 | + public Collection<Shape> getShapes() {
|
|---|
| 1394 | + return Collections.unmodifiableCollection(this.shapes);
|
|---|
| 1395 | + }
|
|---|
| 1396 | +}
|
|---|
| 1397 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypes.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypes.java
|
|---|
| 1398 | new file mode 100644
|
|---|
| 1399 | index 000000000..0dc29c6a6
|
|---|
| 1400 | --- /dev/null
|
|---|
| 1401 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypes.java
|
|---|
| 1402 | @@ -0,0 +1,31 @@
|
|---|
| 1403 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1404 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1405 | +
|
|---|
| 1406 | +/**
|
|---|
| 1407 | + * Geometry types used by Mapbox Vector Tiles
|
|---|
| 1408 | + * @author Taylor Smock
|
|---|
| 1409 | + * @since xxx
|
|---|
| 1410 | + */
|
|---|
| 1411 | +public enum GeometryTypes {
|
|---|
| 1412 | + /** May be ignored */
|
|---|
| 1413 | + UNKNOWN,
|
|---|
| 1414 | + /** May be a point or a multipoint geometry. Uses <i>only</i> {@link Command#MoveTo}. Multiple {@link Command#MoveTo}
|
|---|
| 1415 | + * indicates that it is a multi-point object. */
|
|---|
| 1416 | + POINT,
|
|---|
| 1417 | + /** May be a line or a multiline geometry. Each line {@link Command#MoveTo} and one or more {@link Command#LineTo}. */
|
|---|
| 1418 | + LINESTRING,
|
|---|
| 1419 | + /** May be a polygon or a multipolygon. Each ring uses a {@link Command#MoveTo}, one or more {@link Command#LineTo},
|
|---|
| 1420 | + * and one {@link Command#ClosePath} command. See {@link Ring}s. */
|
|---|
| 1421 | + POLYGON;
|
|---|
| 1422 | +
|
|---|
| 1423 | + /**
|
|---|
| 1424 | + * Rings used by {@link GeometryTypes#POLYGON}
|
|---|
| 1425 | + * @author Taylor Smock
|
|---|
| 1426 | + */
|
|---|
| 1427 | + public enum Ring {
|
|---|
| 1428 | + /** A ring that goes in the clockwise direction */
|
|---|
| 1429 | + ExteriorRing,
|
|---|
| 1430 | + /** A ring that goes in the anti-clockwise direction */
|
|---|
| 1431 | + InteriorRing
|
|---|
| 1432 | + }
|
|---|
| 1433 | +}
|
|---|
| 1434 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java
|
|---|
| 1435 | new file mode 100644
|
|---|
| 1436 | index 000000000..d1186ad3f
|
|---|
| 1437 | --- /dev/null
|
|---|
| 1438 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java
|
|---|
| 1439 | @@ -0,0 +1,25 @@
|
|---|
| 1440 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1441 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1442 | +
|
|---|
| 1443 | +/**
|
|---|
| 1444 | + * Thrown when a mapbox vector tile does not match specifications.
|
|---|
| 1445 | + *
|
|---|
| 1446 | + * @author Taylor Smock
|
|---|
| 1447 | + * @since xxx
|
|---|
| 1448 | + */
|
|---|
| 1449 | +public class InvalidMapboxVectorTileException extends RuntimeException {
|
|---|
| 1450 | + /**
|
|---|
| 1451 | + * Create a default {@link InvalidMapboxVectorTileException}.
|
|---|
| 1452 | + */
|
|---|
| 1453 | + public InvalidMapboxVectorTileException() {
|
|---|
| 1454 | + super();
|
|---|
| 1455 | + }
|
|---|
| 1456 | +
|
|---|
| 1457 | + /**
|
|---|
| 1458 | + * Create a new {@link InvalidMapboxVectorTile} exception with a message
|
|---|
| 1459 | + * @param message The message
|
|---|
| 1460 | + */
|
|---|
| 1461 | + public InvalidMapboxVectorTileException(final String message) {
|
|---|
| 1462 | + super(message);
|
|---|
| 1463 | + }
|
|---|
| 1464 | +}
|
|---|
| 1465 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 1466 | new file mode 100644
|
|---|
| 1467 | index 000000000..09851e8c7
|
|---|
| 1468 | --- /dev/null
|
|---|
| 1469 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 1470 | @@ -0,0 +1,245 @@
|
|---|
| 1471 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1472 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1473 | +import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 1474 | +
|
|---|
| 1475 | +import java.io.IOException;
|
|---|
| 1476 | +import java.util.ArrayList;
|
|---|
| 1477 | +import java.util.Arrays;
|
|---|
| 1478 | +import java.util.Collection;
|
|---|
| 1479 | +import java.util.Collections;
|
|---|
| 1480 | +import java.util.HashSet;
|
|---|
| 1481 | +import java.util.List;
|
|---|
| 1482 | +import java.util.Map;
|
|---|
| 1483 | +import java.util.Objects;
|
|---|
| 1484 | +import java.util.function.Function;
|
|---|
| 1485 | +import java.util.stream.Collectors;
|
|---|
| 1486 | +
|
|---|
| 1487 | +import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 1488 | +import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 1489 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 1490 | +
|
|---|
| 1491 | +/**
|
|---|
| 1492 | + * A Mapbox Vector Tile Layer
|
|---|
| 1493 | + * @author Taylor Smock
|
|---|
| 1494 | + * @since xxx
|
|---|
| 1495 | + */
|
|---|
| 1496 | +public final class Layer {
|
|---|
| 1497 | + private static final class ValueFields<T> {
|
|---|
| 1498 | + static final ValueFields<String> STRING = new ValueFields<>(1, ProtoBufRecord::asString);
|
|---|
| 1499 | + static final ValueFields<Float> FLOAT = new ValueFields<>(2, ProtoBufRecord::asFloat);
|
|---|
| 1500 | + static final ValueFields<Double> DOUBLE = new ValueFields<>(3, ProtoBufRecord::asDouble);
|
|---|
| 1501 | + static final ValueFields<Number> INT64 = new ValueFields<>(4, ProtoBufRecord::asUnsignedVarInt);
|
|---|
| 1502 | + // This may have issues if there are actual uint_values (i.e., more than {@link Long#MAX_VALUE})
|
|---|
| 1503 | + static final ValueFields<Number> UINT64 = new ValueFields<>(5, ProtoBufRecord::asUnsignedVarInt);
|
|---|
| 1504 | + static final ValueFields<Number> SINT64 = new ValueFields<>(6, ProtoBufRecord::asSignedVarInt);
|
|---|
| 1505 | + static final ValueFields<Boolean> BOOL = new ValueFields<>(7, r -> r.asUnsignedVarInt().longValue() != 0);
|
|---|
| 1506 | +
|
|---|
| 1507 | + /**
|
|---|
| 1508 | + * A collection of methods to map a record to a type
|
|---|
| 1509 | + */
|
|---|
| 1510 | + public static final Collection<ValueFields<?>> MAPPERS =
|
|---|
| 1511 | + Collections.unmodifiableList(Arrays.asList(STRING, FLOAT, DOUBLE, INT64, UINT64, SINT64, BOOL));
|
|---|
| 1512 | +
|
|---|
| 1513 | + private final byte field;
|
|---|
| 1514 | + private final Function<ProtoBufRecord, T> conversion;
|
|---|
| 1515 | + private ValueFields(int field, Function<ProtoBufRecord, T> conversion) {
|
|---|
| 1516 | + this.field = (byte) field;
|
|---|
| 1517 | + this.conversion = conversion;
|
|---|
| 1518 | + }
|
|---|
| 1519 | +
|
|---|
| 1520 | + /**
|
|---|
| 1521 | + * Get the field identifier for the value
|
|---|
| 1522 | + * @return The identifier
|
|---|
| 1523 | + */
|
|---|
| 1524 | + public byte getField() {
|
|---|
| 1525 | + return this.field;
|
|---|
| 1526 | + }
|
|---|
| 1527 | +
|
|---|
| 1528 | + /**
|
|---|
| 1529 | + * Convert a protobuf record to a value
|
|---|
| 1530 | + * @param protobufRecord The record to convert
|
|---|
| 1531 | + * @return the converted value
|
|---|
| 1532 | + */
|
|---|
| 1533 | + public T convertValue(ProtoBufRecord protobufRecord) {
|
|---|
| 1534 | + return this.conversion.apply(protobufRecord);
|
|---|
| 1535 | + }
|
|---|
| 1536 | + }
|
|---|
| 1537 | +
|
|---|
| 1538 | + /** The field value for a layer (in {@link ProtoBufRecord#getField}) */
|
|---|
| 1539 | + public static final byte LAYER_FIELD = 3;
|
|---|
| 1540 | + private static final byte VERSION_FIELD = 15;
|
|---|
| 1541 | + private static final byte NAME_FIELD = 1;
|
|---|
| 1542 | + private static final byte FEATURE_FIELD = 2;
|
|---|
| 1543 | + private static final byte KEY_FIELD = 3;
|
|---|
| 1544 | + private static final byte VALUE_FIELD = 4;
|
|---|
| 1545 | + private static final byte EXTENT_FIELD = 5;
|
|---|
| 1546 | + /** The default extent for a vector tile */
|
|---|
| 1547 | + static final int DEFAULT_EXTENT = 4096;
|
|---|
| 1548 | + private static final byte DEFAULT_VERSION = 1;
|
|---|
| 1549 | + /** This is <i>technically</i> an integer, but there are currently only two major versions (1, 2). Required. */
|
|---|
| 1550 | + private final byte version;
|
|---|
| 1551 | + /** A unique name for the layer. This <i>must</i> be unique on a per-tile basis. Required. */
|
|---|
| 1552 | + private final String name;
|
|---|
| 1553 | +
|
|---|
| 1554 | + /** The extent of the tile, typically 4096. Required. */
|
|---|
| 1555 | + private final int extent;
|
|---|
| 1556 | +
|
|---|
| 1557 | + /** A list of unique keys. Order is important. Optional. */
|
|---|
| 1558 | + private final List<String> keyList = new ArrayList<>();
|
|---|
| 1559 | + /** A list of unique values. Order is important. Optional. */
|
|---|
| 1560 | + private final List<Object> valueList = new ArrayList<>();
|
|---|
| 1561 | + /** The actual features of this layer in this tile */
|
|---|
| 1562 | + private final List<Feature> featureCollection;
|
|---|
| 1563 | +
|
|---|
| 1564 | + /**
|
|---|
| 1565 | + * Create a layer from a collection of records
|
|---|
| 1566 | + * @param records The records to convert to a layer
|
|---|
| 1567 | + * @throws IOException - if an IO error occurs
|
|---|
| 1568 | + */
|
|---|
| 1569 | + public Layer(Collection<ProtoBufRecord> records) throws IOException {
|
|---|
| 1570 | + // Do the unique required fields first
|
|---|
| 1571 | + Map<Integer, List<ProtoBufRecord>> sorted = records.stream().collect(Collectors.groupingBy(ProtoBufRecord::getField));
|
|---|
| 1572 | + this.version = sorted.getOrDefault((int) VERSION_FIELD, Collections.emptyList()).parallelStream()
|
|---|
| 1573 | + .map(ProtoBufRecord::asUnsignedVarInt).map(Number::byteValue).findFirst().orElse(DEFAULT_VERSION);
|
|---|
| 1574 | + // Per spec, we cannot continue past this until we have checked the version number
|
|---|
| 1575 | + if (this.version != 1 && this.version != 2) {
|
|---|
| 1576 | + throw new IllegalArgumentException(tr("We do not understand version {0} of the vector tile specification", this.version));
|
|---|
| 1577 | + }
|
|---|
| 1578 | + this.name = sorted.getOrDefault((int) NAME_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asString).findFirst()
|
|---|
| 1579 | + .orElseThrow(() -> new IllegalArgumentException(tr("Vector tile layers must have a layer name")));
|
|---|
| 1580 | + this.extent = sorted.getOrDefault((int) EXTENT_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asSignedVarInt)
|
|---|
| 1581 | + .map(Number::intValue).findAny().orElse(DEFAULT_EXTENT);
|
|---|
| 1582 | +
|
|---|
| 1583 | + sorted.getOrDefault((int) KEY_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asString)
|
|---|
| 1584 | + .forEachOrdered(this.keyList::add);
|
|---|
| 1585 | + sorted.getOrDefault((int) VALUE_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::getBytes)
|
|---|
| 1586 | + .map(ProtoBufParser::new).map(parser1 -> {
|
|---|
| 1587 | + try {
|
|---|
| 1588 | + return new ProtoBufRecord(parser1);
|
|---|
| 1589 | + } catch (IOException e) {
|
|---|
| 1590 | + Logging.error(e);
|
|---|
| 1591 | + return null;
|
|---|
| 1592 | + }
|
|---|
| 1593 | + })
|
|---|
| 1594 | + .filter(Objects::nonNull)
|
|---|
| 1595 | + .map(value -> ValueFields.MAPPERS.parallelStream()
|
|---|
| 1596 | + .filter(v -> v.getField() == value.getField())
|
|---|
| 1597 | + .map(v -> v.convertValue(value)).findFirst()
|
|---|
| 1598 | + .orElseThrow(() -> new IllegalArgumentException(tr("Unknown field in vector tile layer value ({0})", value.getField()))))
|
|---|
| 1599 | + .forEachOrdered(this.valueList::add);
|
|---|
| 1600 | + Collection<IOException> exceptions = new HashSet<>(0);
|
|---|
| 1601 | + this.featureCollection = sorted.getOrDefault((int) FEATURE_FIELD, Collections.emptyList()).parallelStream().map(feature -> {
|
|---|
| 1602 | + try {
|
|---|
| 1603 | + return new Feature(this, feature);
|
|---|
| 1604 | + } catch (IOException e) {
|
|---|
| 1605 | + exceptions.add(e);
|
|---|
| 1606 | + }
|
|---|
| 1607 | + return null;
|
|---|
| 1608 | + }).collect(Collectors.toList());
|
|---|
| 1609 | + if (!exceptions.isEmpty()) {
|
|---|
| 1610 | + throw exceptions.iterator().next();
|
|---|
| 1611 | + }
|
|---|
| 1612 | + // Cleanup bytes (for memory)
|
|---|
| 1613 | + for (ProtoBufRecord record : records) {
|
|---|
| 1614 | + record.close();
|
|---|
| 1615 | + }
|
|---|
| 1616 | + }
|
|---|
| 1617 | +
|
|---|
| 1618 | + /**
|
|---|
| 1619 | + * Get all the records from a array of bytes
|
|---|
| 1620 | + * @param bytes The byte information
|
|---|
| 1621 | + * @return All the protobuf records
|
|---|
| 1622 | + * @throws IOException If there was an error reading the bytes (unlikely)
|
|---|
| 1623 | + */
|
|---|
| 1624 | + private static Collection<ProtoBufRecord> getAllRecords(byte[] bytes) throws IOException {
|
|---|
| 1625 | + try (ProtoBufParser parser = new ProtoBufParser(bytes)) {
|
|---|
| 1626 | + return parser.allRecords();
|
|---|
| 1627 | + }
|
|---|
| 1628 | + }
|
|---|
| 1629 | +
|
|---|
| 1630 | + /**
|
|---|
| 1631 | + * Create a new layer
|
|---|
| 1632 | + * @param bytes The bytes that the layer comes from
|
|---|
| 1633 | + * @throws IOException - if an IO error occurs
|
|---|
| 1634 | + */
|
|---|
| 1635 | + public Layer(byte[] bytes) throws IOException {
|
|---|
| 1636 | + this(getAllRecords(bytes));
|
|---|
| 1637 | + }
|
|---|
| 1638 | +
|
|---|
| 1639 | + /**
|
|---|
| 1640 | + * Get the extent of the tile
|
|---|
| 1641 | + * @return The layer extent
|
|---|
| 1642 | + */
|
|---|
| 1643 | + public int getExtent() {
|
|---|
| 1644 | + return this.extent;
|
|---|
| 1645 | + }
|
|---|
| 1646 | +
|
|---|
| 1647 | + /**
|
|---|
| 1648 | + * Get the feature on this layer
|
|---|
| 1649 | + * @return the features
|
|---|
| 1650 | + */
|
|---|
| 1651 | + public Collection<Feature> getFeatures() {
|
|---|
| 1652 | + return Collections.unmodifiableCollection(this.featureCollection);
|
|---|
| 1653 | + }
|
|---|
| 1654 | +
|
|---|
| 1655 | + /**
|
|---|
| 1656 | + * Get the geometry for this layer
|
|---|
| 1657 | + * @return The geometry
|
|---|
| 1658 | + */
|
|---|
| 1659 | + public Collection<Geometry> getGeometry() {
|
|---|
| 1660 | + return getFeatures().stream().map(Feature::getGeometryObject).collect(Collectors.toList());
|
|---|
| 1661 | + }
|
|---|
| 1662 | +
|
|---|
| 1663 | + /**
|
|---|
| 1664 | + * Get a specified key
|
|---|
| 1665 | + * @param index The index in the key list
|
|---|
| 1666 | + * @return The actual key
|
|---|
| 1667 | + */
|
|---|
| 1668 | + public String getKey(int index) {
|
|---|
| 1669 | + return this.keyList.get(index);
|
|---|
| 1670 | + }
|
|---|
| 1671 | +
|
|---|
| 1672 | + /**
|
|---|
| 1673 | + * Get the name of the layer
|
|---|
| 1674 | + * @return The layer name
|
|---|
| 1675 | + */
|
|---|
| 1676 | + public String getName() {
|
|---|
| 1677 | + return this.name;
|
|---|
| 1678 | + }
|
|---|
| 1679 | +
|
|---|
| 1680 | + /**
|
|---|
| 1681 | + * Get a specified value
|
|---|
| 1682 | + * @param index The index in the value list
|
|---|
| 1683 | + * @return The actual value. This can be a {@link String}, {@link Boolean}, {@link Integer}, or {@link Float} value.
|
|---|
| 1684 | + */
|
|---|
| 1685 | + public Object getValue(int index) {
|
|---|
| 1686 | + return this.valueList.get(index);
|
|---|
| 1687 | + }
|
|---|
| 1688 | +
|
|---|
| 1689 | + /**
|
|---|
| 1690 | + * Get the MapBox Vector Tile version specification for this layer
|
|---|
| 1691 | + * @return The version of the MapBox Vector Tile specification
|
|---|
| 1692 | + */
|
|---|
| 1693 | + public byte getVersion() {
|
|---|
| 1694 | + return this.version;
|
|---|
| 1695 | + }
|
|---|
| 1696 | +
|
|---|
| 1697 | + @Override
|
|---|
| 1698 | + public boolean equals(Object other) {
|
|---|
| 1699 | + if (other instanceof Layer) {
|
|---|
| 1700 | + Layer o = (Layer) other;
|
|---|
| 1701 | + return this.extent == o.extent
|
|---|
| 1702 | + && this.version == o.version
|
|---|
| 1703 | + && Objects.equals(this.name, o.name)
|
|---|
| 1704 | + && Objects.equals(this.featureCollection, o.featureCollection)
|
|---|
| 1705 | + && Objects.equals(this.keyList, o.keyList)
|
|---|
| 1706 | + && Objects.equals(this.valueList, o.valueList);
|
|---|
| 1707 | + }
|
|---|
| 1708 | + return false;
|
|---|
| 1709 | + }
|
|---|
| 1710 | +
|
|---|
| 1711 | + @Override
|
|---|
| 1712 | + public int hashCode() {
|
|---|
| 1713 | + return Objects.hash(this.name, this.version, this.extent, this.featureCollection, this.keyList, this.valueList);
|
|---|
| 1714 | + }
|
|---|
| 1715 | +}
|
|---|
| 1716 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java
|
|---|
| 1717 | new file mode 100644
|
|---|
| 1718 | index 000000000..84ac8ae89
|
|---|
| 1719 | --- /dev/null
|
|---|
| 1720 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java
|
|---|
| 1721 | @@ -0,0 +1,34 @@
|
|---|
| 1722 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1723 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1724 | +
|
|---|
| 1725 | +import java.util.Arrays;
|
|---|
| 1726 | +import java.util.Collections;
|
|---|
| 1727 | +import java.util.List;
|
|---|
| 1728 | +
|
|---|
| 1729 | +/**
|
|---|
| 1730 | + * Items that MAY be used to figure out if a file or server response MAY BE a Mapbox Vector Tile
|
|---|
| 1731 | + * @author Taylor Smock
|
|---|
| 1732 | + * @since xxx
|
|---|
| 1733 | + */
|
|---|
| 1734 | +public final class MVTFile {
|
|---|
| 1735 | + /**
|
|---|
| 1736 | + * Extensions for Mapbox Vector Tiles.
|
|---|
| 1737 | + * This is a SHOULD, <i>not</i> a MUST.
|
|---|
| 1738 | + */
|
|---|
| 1739 | + public static final List<String> EXTENSION = Collections.unmodifiableList(Arrays.asList("mvt"));
|
|---|
| 1740 | +
|
|---|
| 1741 | + /**
|
|---|
| 1742 | + * mimetypes for Mapbox Vector Tiles
|
|---|
| 1743 | + * This is a SHOULD, <i>not</i> a MUST.
|
|---|
| 1744 | + */
|
|---|
| 1745 | + public static final List<String> MIMETYPE = Collections.unmodifiableList(Arrays.asList("application/vnd.mapbox-vector-tile"));
|
|---|
| 1746 | +
|
|---|
| 1747 | + /**
|
|---|
| 1748 | + * The default projection. This is Web Mercator, per specification.
|
|---|
| 1749 | + */
|
|---|
| 1750 | + public static final String DEFAULT_PROJECTION = "EPSG:3857";
|
|---|
| 1751 | +
|
|---|
| 1752 | + private MVTFile() {
|
|---|
| 1753 | + // Hide the constructor
|
|---|
| 1754 | + }
|
|---|
| 1755 | +}
|
|---|
| 1756 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 1757 | new file mode 100644
|
|---|
| 1758 | index 000000000..5d1d781dd
|
|---|
| 1759 | --- /dev/null
|
|---|
| 1760 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 1761 | @@ -0,0 +1,119 @@
|
|---|
| 1762 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1763 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1764 | +
|
|---|
| 1765 | +import java.awt.image.BufferedImage;
|
|---|
| 1766 | +import java.io.IOException;
|
|---|
| 1767 | +import java.io.InputStream;
|
|---|
| 1768 | +import java.util.Collection;
|
|---|
| 1769 | +import java.util.HashSet;
|
|---|
| 1770 | +import java.util.List;
|
|---|
| 1771 | +import java.util.stream.Collectors;
|
|---|
| 1772 | +
|
|---|
| 1773 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 1774 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
|
|---|
| 1775 | +import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 1776 | +import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 1777 | +import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 1778 | +import org.openstreetmap.josm.tools.ListenerList;
|
|---|
| 1779 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 1780 | +
|
|---|
| 1781 | +/**
|
|---|
| 1782 | + * A class for MapBox Vector Tiles
|
|---|
| 1783 | + *
|
|---|
| 1784 | + * @author Taylor Smock
|
|---|
| 1785 | + * @since xxx
|
|---|
| 1786 | + */
|
|---|
| 1787 | +public class MVTTile extends Tile implements VectorTile {
|
|---|
| 1788 | + private final ListenerList<TileListener> listenerList = ListenerList.create();
|
|---|
| 1789 | + private Collection<Layer> layers;
|
|---|
| 1790 | + private int extent = Layer.DEFAULT_EXTENT;
|
|---|
| 1791 | + static final BufferedImage CLEAR_LOADED = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
|
|---|
| 1792 | +
|
|---|
| 1793 | + /**
|
|---|
| 1794 | + * Create a new Tile
|
|---|
| 1795 | + * @param source The source of the tile
|
|---|
| 1796 | + * @param xtile The x coordinate for the tile
|
|---|
| 1797 | + * @param ytile The y coordinate for the tile
|
|---|
| 1798 | + * @param zoom The zoom for the tile
|
|---|
| 1799 | + */
|
|---|
| 1800 | + public MVTTile(TileSource source, int xtile, int ytile, int zoom) {
|
|---|
| 1801 | + super(source, xtile, ytile, zoom);
|
|---|
| 1802 | + }
|
|---|
| 1803 | +
|
|---|
| 1804 | + @Override
|
|---|
| 1805 | + public void loadImage(final InputStream inputStream) throws IOException {
|
|---|
| 1806 | + if (this.image == null || this.image == Tile.LOADING_IMAGE || this.image == Tile.ERROR_IMAGE) {
|
|---|
| 1807 | + this.initLoading();
|
|---|
| 1808 | + ProtoBufParser parser = new ProtoBufParser(inputStream);
|
|---|
| 1809 | + Collection<ProtoBufRecord> protoBufRecords = parser.allRecords();
|
|---|
| 1810 | + this.layers = new HashSet<>();
|
|---|
| 1811 | + this.layers = protoBufRecords.stream().map(record -> {
|
|---|
| 1812 | + Layer mvtLayer = null;
|
|---|
| 1813 | + if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 1814 | + try (ProtoBufParser tParser = new ProtoBufParser(record.getBytes())) {
|
|---|
| 1815 | + mvtLayer = new Layer(tParser.allRecords());
|
|---|
| 1816 | + } catch (IOException e) {
|
|---|
| 1817 | + Logging.error(e);
|
|---|
| 1818 | + } finally {
|
|---|
| 1819 | + // Cleanup bytes
|
|---|
| 1820 | + record.close();
|
|---|
| 1821 | + }
|
|---|
| 1822 | + }
|
|---|
| 1823 | + return mvtLayer;
|
|---|
| 1824 | + }).collect(Collectors.toCollection(HashSet::new));
|
|---|
| 1825 | + this.extent = layers.stream().map(Layer::getExtent).max(Integer::compare).orElse(Layer.DEFAULT_EXTENT);
|
|---|
| 1826 | + this.finishLoading();
|
|---|
| 1827 | + this.listenerList.fireEvent(event -> event.finishedLoading(this));
|
|---|
| 1828 | + // Ensure that we don't keep the loading image around
|
|---|
| 1829 | + this.image = CLEAR_LOADED;
|
|---|
| 1830 | + }
|
|---|
| 1831 | + }
|
|---|
| 1832 | +
|
|---|
| 1833 | + @Override
|
|---|
| 1834 | + public Collection<Layer> getLayers() {
|
|---|
| 1835 | + return this.layers;
|
|---|
| 1836 | + }
|
|---|
| 1837 | +
|
|---|
| 1838 | + @Override
|
|---|
| 1839 | + public int getExtent() {
|
|---|
| 1840 | + return this.extent;
|
|---|
| 1841 | + }
|
|---|
| 1842 | +
|
|---|
| 1843 | + /**
|
|---|
| 1844 | + * Add a tile loader finisher listener
|
|---|
| 1845 | + *
|
|---|
| 1846 | + * @param listener The listener to add
|
|---|
| 1847 | + */
|
|---|
| 1848 | + public void addTileLoaderFinisher(TileListener listener) {
|
|---|
| 1849 | + // Add as weak listeners since we don't want to keep unnecessary references.
|
|---|
| 1850 | + this.listenerList.addWeakListener(listener);
|
|---|
| 1851 | + }
|
|---|
| 1852 | +
|
|---|
| 1853 | + /**
|
|---|
| 1854 | + * A class that can be notified that a tile has finished loading
|
|---|
| 1855 | + *
|
|---|
| 1856 | + * @author Taylor Smock
|
|---|
| 1857 | + */
|
|---|
| 1858 | + public interface TileListener {
|
|---|
| 1859 | + /**
|
|---|
| 1860 | + * Called when the MVTTile is finished loading
|
|---|
| 1861 | + *
|
|---|
| 1862 | + * @param tile The tile that finished loading
|
|---|
| 1863 | + */
|
|---|
| 1864 | + void finishedLoading(MVTTile tile);
|
|---|
| 1865 | + }
|
|---|
| 1866 | +
|
|---|
| 1867 | + /**
|
|---|
| 1868 | + * A class used to set the layers that an MVTTile will show.
|
|---|
| 1869 | + *
|
|---|
| 1870 | + * @author Taylor Smock
|
|---|
| 1871 | + */
|
|---|
| 1872 | + public interface LayerShower {
|
|---|
| 1873 | + /**
|
|---|
| 1874 | + * Get a list of layers to show
|
|---|
| 1875 | + *
|
|---|
| 1876 | + * @return A list of layer names
|
|---|
| 1877 | + */
|
|---|
| 1878 | + List<String> layersToShow();
|
|---|
| 1879 | + }
|
|---|
| 1880 | +}
|
|---|
| 1881 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoader.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoader.java
|
|---|
| 1882 | new file mode 100644
|
|---|
| 1883 | index 000000000..bf1b368d9
|
|---|
| 1884 | --- /dev/null
|
|---|
| 1885 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoader.java
|
|---|
| 1886 | @@ -0,0 +1,79 @@
|
|---|
| 1887 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1888 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1889 | +
|
|---|
| 1890 | +import java.util.concurrent.ThreadPoolExecutor;
|
|---|
| 1891 | +
|
|---|
| 1892 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 1893 | +import org.openstreetmap.gui.jmapviewer.interfaces.CachedTileLoader;
|
|---|
| 1894 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
|
|---|
| 1895 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
|
|---|
| 1896 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
|
|---|
| 1897 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
|
|---|
| 1898 | +import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
|
|---|
| 1899 | +import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob;
|
|---|
| 1900 | +import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
|
|---|
| 1901 | +import org.openstreetmap.josm.data.imagery.TileJobOptions;
|
|---|
| 1902 | +import org.openstreetmap.josm.data.preferences.IntegerProperty;
|
|---|
| 1903 | +import org.openstreetmap.josm.tools.CheckParameterUtil;
|
|---|
| 1904 | +
|
|---|
| 1905 | +import org.apache.commons.jcs3.access.behavior.ICacheAccess;
|
|---|
| 1906 | +
|
|---|
| 1907 | +/**
|
|---|
| 1908 | + * A TileLoader class for MVT tiles
|
|---|
| 1909 | + * @author Taylor Smock
|
|---|
| 1910 | + * @since xxx
|
|---|
| 1911 | + */
|
|---|
| 1912 | +public class MapBoxVectorCachedTileLoader implements TileLoader, CachedTileLoader {
|
|---|
| 1913 | + protected final ICacheAccess<String, BufferedImageCacheEntry> cache;
|
|---|
| 1914 | + protected final TileLoaderListener listener;
|
|---|
| 1915 | + protected final TileJobOptions options;
|
|---|
| 1916 | + private static final IntegerProperty THREAD_LIMIT =
|
|---|
| 1917 | + new IntegerProperty("imagery.vector.mvtloader.maxjobs", TMSCachedTileLoader.THREAD_LIMIT.getDefaultValue());
|
|---|
| 1918 | + private static final ThreadPoolExecutor DEFAULT_DOWNLOAD_JOB_DISPATCHER =
|
|---|
| 1919 | + TMSCachedTileLoader.getNewThreadPoolExecutor("MVT-downloader-%d", THREAD_LIMIT.get());
|
|---|
| 1920 | +
|
|---|
| 1921 | + /**
|
|---|
| 1922 | + * Constructor
|
|---|
| 1923 | + * @param listener called when tile loading has finished
|
|---|
| 1924 | + * @param cache of the cache
|
|---|
| 1925 | + * @param options tile job options
|
|---|
| 1926 | + */
|
|---|
| 1927 | + public MapBoxVectorCachedTileLoader(TileLoaderListener listener, ICacheAccess<String, BufferedImageCacheEntry> cache,
|
|---|
| 1928 | + TileJobOptions options) {
|
|---|
| 1929 | + CheckParameterUtil.ensureParameterNotNull(cache, "cache");
|
|---|
| 1930 | + this.cache = cache;
|
|---|
| 1931 | + this.options = options;
|
|---|
| 1932 | + this.listener = listener;
|
|---|
| 1933 | + }
|
|---|
| 1934 | +
|
|---|
| 1935 | + @Override
|
|---|
| 1936 | + public void clearCache(TileSource source) {
|
|---|
| 1937 | + this.cache.remove(source.getName() + ':');
|
|---|
| 1938 | + }
|
|---|
| 1939 | +
|
|---|
| 1940 | + @Override
|
|---|
| 1941 | + public TileJob createTileLoaderJob(Tile tile) {
|
|---|
| 1942 | + return new MapBoxVectorCachedTileLoaderJob(
|
|---|
| 1943 | + listener,
|
|---|
| 1944 | + tile,
|
|---|
| 1945 | + cache,
|
|---|
| 1946 | + options,
|
|---|
| 1947 | + getDownloadExecutor());
|
|---|
| 1948 | + }
|
|---|
| 1949 | +
|
|---|
| 1950 | + @Override
|
|---|
| 1951 | + public void cancelOutstandingTasks() {
|
|---|
| 1952 | + final ThreadPoolExecutor executor = getDownloadExecutor();
|
|---|
| 1953 | + executor.getQueue().stream().filter(executor::remove).filter(MapBoxVectorCachedTileLoaderJob.class::isInstance)
|
|---|
| 1954 | + .map(MapBoxVectorCachedTileLoaderJob.class::cast).forEach(JCSCachedTileLoaderJob::handleJobCancellation);
|
|---|
| 1955 | + }
|
|---|
| 1956 | +
|
|---|
| 1957 | + @Override
|
|---|
| 1958 | + public boolean hasOutstandingTasks() {
|
|---|
| 1959 | + return getDownloadExecutor().getTaskCount() > getDownloadExecutor().getCompletedTaskCount();
|
|---|
| 1960 | + }
|
|---|
| 1961 | +
|
|---|
| 1962 | + private static ThreadPoolExecutor getDownloadExecutor() {
|
|---|
| 1963 | + return DEFAULT_DOWNLOAD_JOB_DISPATCHER;
|
|---|
| 1964 | + }
|
|---|
| 1965 | +}
|
|---|
| 1966 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoaderJob.java
|
|---|
| 1967 | new file mode 100644
|
|---|
| 1968 | index 000000000..748172f5f
|
|---|
| 1969 | --- /dev/null
|
|---|
| 1970 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoaderJob.java
|
|---|
| 1971 | @@ -0,0 +1,26 @@
|
|---|
| 1972 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 1973 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 1974 | +
|
|---|
| 1975 | +import java.util.concurrent.ThreadPoolExecutor;
|
|---|
| 1976 | +
|
|---|
| 1977 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 1978 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
|
|---|
| 1979 | +import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
|
|---|
| 1980 | +import org.openstreetmap.josm.data.imagery.TMSCachedTileLoaderJob;
|
|---|
| 1981 | +import org.openstreetmap.josm.data.imagery.TileJobOptions;
|
|---|
| 1982 | +
|
|---|
| 1983 | +import org.apache.commons.jcs3.access.behavior.ICacheAccess;
|
|---|
| 1984 | +
|
|---|
| 1985 | +/**
|
|---|
| 1986 | + * Bridge to JCS cache for MVT tiles
|
|---|
| 1987 | + * @author Taylor Smock
|
|---|
| 1988 | + * @since xxx
|
|---|
| 1989 | + */
|
|---|
| 1990 | +public class MapBoxVectorCachedTileLoaderJob extends TMSCachedTileLoaderJob {
|
|---|
| 1991 | +
|
|---|
| 1992 | + public MapBoxVectorCachedTileLoaderJob(TileLoaderListener listener, Tile tile,
|
|---|
| 1993 | + ICacheAccess<String, BufferedImageCacheEntry> cache, TileJobOptions options,
|
|---|
| 1994 | + ThreadPoolExecutor downloadExecutor) {
|
|---|
| 1995 | + super(listener, tile, cache, options, downloadExecutor);
|
|---|
| 1996 | + }
|
|---|
| 1997 | +}
|
|---|
| 1998 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 1999 | new file mode 100644
|
|---|
| 2000 | index 000000000..413c7b32b
|
|---|
| 2001 | --- /dev/null
|
|---|
| 2002 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 2003 | @@ -0,0 +1,92 @@
|
|---|
| 2004 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2005 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 2006 | +import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 2007 | +
|
|---|
| 2008 | +import java.io.IOException;
|
|---|
| 2009 | +import java.io.InputStream;
|
|---|
| 2010 | +import java.util.List;
|
|---|
| 2011 | +import java.util.Objects;
|
|---|
| 2012 | +import java.util.stream.Collectors;
|
|---|
| 2013 | +
|
|---|
| 2014 | +import javax.json.Json;
|
|---|
| 2015 | +import javax.json.JsonException;
|
|---|
| 2016 | +import javax.json.JsonReader;
|
|---|
| 2017 | +
|
|---|
| 2018 | +import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 2019 | +import org.openstreetmap.josm.data.imagery.JosmTemplatedTMSTileSource;
|
|---|
| 2020 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapBoxVectorStyle;
|
|---|
| 2021 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.Source;
|
|---|
| 2022 | +import org.openstreetmap.josm.gui.ExtendedDialog;
|
|---|
| 2023 | +import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 2024 | +import org.openstreetmap.josm.gui.util.GuiHelper;
|
|---|
| 2025 | +import org.openstreetmap.josm.gui.widgets.JosmComboBox;
|
|---|
| 2026 | +import org.openstreetmap.josm.io.CachedFile;
|
|---|
| 2027 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 2028 | +
|
|---|
| 2029 | +/**
|
|---|
| 2030 | + * Tile Source handling for Mapbox Vector Tile sources
|
|---|
| 2031 | + * @author Taylor Smock
|
|---|
| 2032 | + * @since xxx
|
|---|
| 2033 | + */
|
|---|
| 2034 | +public class MapboxVectorTileSource extends JosmTemplatedTMSTileSource {
|
|---|
| 2035 | + private final MapBoxVectorStyle styleSource;
|
|---|
| 2036 | +
|
|---|
| 2037 | + /**
|
|---|
| 2038 | + * Create a new {@link MapboxVectorTileSource} from an {@link ImageryInfo}
|
|---|
| 2039 | + * @param info The info to create the source from
|
|---|
| 2040 | + */
|
|---|
| 2041 | + public MapboxVectorTileSource(ImageryInfo info) {
|
|---|
| 2042 | + super(info);
|
|---|
| 2043 | + MapBoxVectorStyle mapBoxVectorStyle = null;
|
|---|
| 2044 | + try (CachedFile style = new CachedFile(info.getUrl());
|
|---|
| 2045 | + InputStream inputStream = style.getInputStream();
|
|---|
| 2046 | + JsonReader reader = Json.createReader(inputStream)) {
|
|---|
| 2047 | + reader.readObject();
|
|---|
| 2048 | + // OK, we have a stylesheet
|
|---|
| 2049 | + mapBoxVectorStyle = MapBoxVectorStyle.getMapBoxVectorStyle(info.getUrl());
|
|---|
| 2050 | + } catch (IOException | JsonException e) {
|
|---|
| 2051 | + Logging.trace(e);
|
|---|
| 2052 | + }
|
|---|
| 2053 | + this.styleSource = mapBoxVectorStyle;
|
|---|
| 2054 | + if (this.styleSource != null) {
|
|---|
| 2055 | + final Source source;
|
|---|
| 2056 | + List<Source> sources = this.styleSource.getSources().keySet().stream().filter(Objects::nonNull)
|
|---|
| 2057 | + .collect(Collectors.toList());
|
|---|
| 2058 | + if (sources.size() == 1) {
|
|---|
| 2059 | + source = sources.get(0);
|
|---|
| 2060 | + } else if (!sources.isEmpty()) {
|
|---|
| 2061 | + // Ask user what source they want.
|
|---|
| 2062 | + source = GuiHelper.runInEDTAndWaitAndReturn(() -> {
|
|---|
| 2063 | + ExtendedDialog dialog = new ExtendedDialog(MainApplication.getMainFrame(),
|
|---|
| 2064 | + tr("Select Vector Tile Layers"), tr("Add layers"));
|
|---|
| 2065 | + JosmComboBox<Source> comboBox = new JosmComboBox<>(sources.toArray(new Source[0]));
|
|---|
| 2066 | + comboBox.setSelectedIndex(0);
|
|---|
| 2067 | + dialog.setContent(comboBox);
|
|---|
| 2068 | + dialog.showDialog();
|
|---|
| 2069 | + return (Source) comboBox.getSelectedItem();
|
|---|
| 2070 | + });
|
|---|
| 2071 | + } else {
|
|---|
| 2072 | + // Umm. What happened? We probably have an invalid style source.
|
|---|
| 2073 | + throw new InvalidMapboxVectorTileException(tr("Cannot understand style source: {0}", info.getUrl()));
|
|---|
| 2074 | + }
|
|---|
| 2075 | + if (source != null) {
|
|---|
| 2076 | + this.name = name + ": " + source.getName();
|
|---|
| 2077 | + // There can technically be multiple URL's for this field; unfortunately, JOSM can only handle one right now.
|
|---|
| 2078 | + this.baseUrl = source.getUrls().get(0);
|
|---|
| 2079 | + this.minZoom = source.getMinZoom();
|
|---|
| 2080 | + this.maxZoom = source.getMaxZoom();
|
|---|
| 2081 | + if (source.getAttributionText() != null) {
|
|---|
| 2082 | + this.setAttributionText(source.getAttributionText());
|
|---|
| 2083 | + }
|
|---|
| 2084 | + }
|
|---|
| 2085 | + }
|
|---|
| 2086 | + }
|
|---|
| 2087 | +
|
|---|
| 2088 | + /**
|
|---|
| 2089 | + * Get the style source for this Vector Tile source
|
|---|
| 2090 | + * @return The source to use for styling
|
|---|
| 2091 | + */
|
|---|
| 2092 | + public MapBoxVectorStyle getStyleSource() {
|
|---|
| 2093 | + return this.styleSource;
|
|---|
| 2094 | + }
|
|---|
| 2095 | +}
|
|---|
| 2096 | diff --git a/test/data/mapillary.json b/test/data/mapillary.json
|
|---|
| 2097 | new file mode 100644
|
|---|
| 2098 | index 000000000..0f6f9483d
|
|---|
| 2099 | --- /dev/null
|
|---|
| 2100 | +++ b/test/data/mapillary.json
|
|---|
| 2101 | @@ -0,0 +1,111 @@
|
|---|
| 2102 | +{
|
|---|
| 2103 | + "version":8,
|
|---|
| 2104 | + "name":"Mapillary",
|
|---|
| 2105 | + "owner":"Mapillary",
|
|---|
| 2106 | + "id":"mapillary",
|
|---|
| 2107 | + "sources":{
|
|---|
| 2108 | + "mapillary-source":{
|
|---|
| 2109 | + "type":"vector",
|
|---|
| 2110 | + "tiles":[
|
|---|
| 2111 | + "https://tiles3.mapillary.com/v0.1/{z}/{x}/{y}.mvt"
|
|---|
| 2112 | + ],
|
|---|
| 2113 | + "maxzoom":14
|
|---|
| 2114 | + },
|
|---|
| 2115 | + "mapillary-features-source": {
|
|---|
| 2116 | + "maxzoom": 20,
|
|---|
| 2117 | + "minzoom": 14,
|
|---|
| 2118 | + "tiles": [ "https://a.mapillary.com/v3/map_features?tile={z}/{x}/{y}&client_id=_apiKey_&layers=points&per_page=1000" ],
|
|---|
| 2119 | + "type": "vector"
|
|---|
| 2120 | + },
|
|---|
| 2121 | + "mapillary-traffic-signs-source": {
|
|---|
| 2122 | + "maxzoom": 20,
|
|---|
| 2123 | + "minzoom": 14,
|
|---|
| 2124 | + "tiles": [ "https://a.mapillary.com/v3/map_features?tile={z}/{x}/{y}&client_id=_apiKey_&layers=trafficsigns&per_page=1000" ],
|
|---|
| 2125 | + "type": "vector"
|
|---|
| 2126 | + }
|
|---|
| 2127 | + },
|
|---|
| 2128 | + "layers":[
|
|---|
| 2129 | + {
|
|---|
| 2130 | + "filter": [ "==", "pano", 1 ],
|
|---|
| 2131 | + "id": "mapillary-panos",
|
|---|
| 2132 | + "type": "circle",
|
|---|
| 2133 | + "source": "mapillary-source",
|
|---|
| 2134 | + "source-layer": "mapillary-images",
|
|---|
| 2135 | + "minzoom": 17,
|
|---|
| 2136 | + "paint": {
|
|---|
| 2137 | + "circle-color": "#05CB63",
|
|---|
| 2138 | + "circle-opacity": 0.5,
|
|---|
| 2139 | + "circle-radius": 18
|
|---|
| 2140 | + }
|
|---|
| 2141 | + },
|
|---|
| 2142 | + {
|
|---|
| 2143 | + "id": "mapillary-dots",
|
|---|
| 2144 | + "type": "circle",
|
|---|
| 2145 | + "source": "mapillary-source",
|
|---|
| 2146 | + "source-layer": "mapillary-images",
|
|---|
| 2147 | + "interactive": true,
|
|---|
| 2148 | + "minzoom": 14,
|
|---|
| 2149 | + "paint": {
|
|---|
| 2150 | + "circle-color": "#05CB63",
|
|---|
| 2151 | + "circle-radius": 6
|
|---|
| 2152 | + }
|
|---|
| 2153 | + },
|
|---|
| 2154 | + {
|
|---|
| 2155 | + "id": "mapillary-lines",
|
|---|
| 2156 | + "type": "line",
|
|---|
| 2157 | + "source": "mapillary-source",
|
|---|
| 2158 | + "source-layer": "mapillary-sequences",
|
|---|
| 2159 | + "minzoom": 6,
|
|---|
| 2160 | + "paint": {
|
|---|
| 2161 | + "line-color": "#05CB63",
|
|---|
| 2162 | + "line-width": 2
|
|---|
| 2163 | + }
|
|---|
| 2164 | + },
|
|---|
| 2165 | + {
|
|---|
| 2166 | + "id": "mapillary-overview",
|
|---|
| 2167 | + "type": "circle",
|
|---|
| 2168 | + "source": "mapillary-source",
|
|---|
| 2169 | + "source-layer": "mapillary-sequence-overview",
|
|---|
| 2170 | + "maxzoom": 6,
|
|---|
| 2171 | + "paint": {
|
|---|
| 2172 | + "circle-radius": 4,
|
|---|
| 2173 | + "circle-opacity": 0.6,
|
|---|
| 2174 | + "circle-color": "#05CB63"
|
|---|
| 2175 | + }
|
|---|
| 2176 | + },
|
|---|
| 2177 | + {
|
|---|
| 2178 | + "id": "mapillary-features",
|
|---|
| 2179 | + "type": "symbol",
|
|---|
| 2180 | + "source": "mapillary-features-source",
|
|---|
| 2181 | + "source-layer": "mapillary-map-features",
|
|---|
| 2182 | + "interactive": true,
|
|---|
| 2183 | + "minzoom": 14,
|
|---|
| 2184 | + "layout": {
|
|---|
| 2185 | + "icon-image": "{value}",
|
|---|
| 2186 | + "icon-allow-overlap": true,
|
|---|
| 2187 | + "symbol-avoid-edges": true
|
|---|
| 2188 | + },
|
|---|
| 2189 | + "paint": {
|
|---|
| 2190 | + "text-color": "#fff",
|
|---|
| 2191 | + "text-halo-color": "#000"
|
|---|
| 2192 | + }
|
|---|
| 2193 | + },
|
|---|
| 2194 | + {
|
|---|
| 2195 | + "id": "mapillary-traffic-signs",
|
|---|
| 2196 | + "type": "symbol",
|
|---|
| 2197 | + "source": "mapillary-traffic-signs-source",
|
|---|
| 2198 | + "source-layer": "mapillary-map-features",
|
|---|
| 2199 | + "interactive": true,
|
|---|
| 2200 | + "minzoom": 14,
|
|---|
| 2201 | + "layout": {
|
|---|
| 2202 | + "icon-image": "{value}",
|
|---|
| 2203 | + "icon-allow-overlap": true,
|
|---|
| 2204 | + "symbol-avoid-edges": true
|
|---|
| 2205 | + },
|
|---|
| 2206 | + "paint": {
|
|---|
| 2207 | + "text-color": "#fff",
|
|---|
| 2208 | + "text-halo-color": "#000"
|
|---|
| 2209 | + }
|
|---|
| 2210 | + }
|
|---|
| 2211 | + ]
|
|---|
| 2212 | +}
|
|---|
| 2213 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java
|
|---|
| 2214 | new file mode 100644
|
|---|
| 2215 | index 000000000..5468fe649
|
|---|
| 2216 | --- /dev/null
|
|---|
| 2217 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java
|
|---|
| 2218 | @@ -0,0 +1,122 @@
|
|---|
| 2219 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2220 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 2221 | +
|
|---|
| 2222 | +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|---|
| 2223 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 2224 | +import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|---|
| 2225 | +import static org.junit.jupiter.api.Assertions.assertSame;
|
|---|
| 2226 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 2227 | +
|
|---|
| 2228 | +
|
|---|
| 2229 | +import static org.openstreetmap.josm.data.imagery.vectortile.mapbox.LayerTest.getSimpleFeatureLayerBytes;
|
|---|
| 2230 | +import static org.openstreetmap.josm.data.imagery.vectortile.mapbox.LayerTest.getLayer;
|
|---|
| 2231 | +
|
|---|
| 2232 | +import java.text.NumberFormat;
|
|---|
| 2233 | +import java.util.Arrays;
|
|---|
| 2234 | +
|
|---|
| 2235 | +import org.junit.jupiter.api.Test;
|
|---|
| 2236 | +
|
|---|
| 2237 | +/**
|
|---|
| 2238 | + * Test class for {@link Feature}
|
|---|
| 2239 | + */
|
|---|
| 2240 | +class FeatureTest {
|
|---|
| 2241 | + /**
|
|---|
| 2242 | + * This can be used to replace bytes 11-14 (inclusive) in {@link LayerTest#simpleFeatureLayerBytes}.
|
|---|
| 2243 | + */
|
|---|
| 2244 | + private final byte[] nonPackedTags = new byte[] {0x10, 0x00, 0x10, 0x00};
|
|---|
| 2245 | +
|
|---|
| 2246 | + @Test
|
|---|
| 2247 | + void testCreation() {
|
|---|
| 2248 | + testCreation(getSimpleFeatureLayerBytes());
|
|---|
| 2249 | + }
|
|---|
| 2250 | +
|
|---|
| 2251 | + @Test
|
|---|
| 2252 | + void testCreationUnpacked() {
|
|---|
| 2253 | + byte[] copyBytes = getSimpleFeatureLayerBytes();
|
|---|
| 2254 | + System.arraycopy(nonPackedTags, 0, copyBytes, 13, nonPackedTags.length);
|
|---|
| 2255 | + testCreation(copyBytes);
|
|---|
| 2256 | + }
|
|---|
| 2257 | +
|
|---|
| 2258 | + @Test
|
|---|
| 2259 | + void testCreationTrueToFalse() {
|
|---|
| 2260 | + byte[] copyBytes = getSimpleFeatureLayerBytes();
|
|---|
| 2261 | + copyBytes[copyBytes.length - 1] = 0x00; // set value=false
|
|---|
| 2262 | + Layer layer = assertDoesNotThrow(() -> getLayer(copyBytes));
|
|---|
| 2263 | + assertSame(Boolean.FALSE, layer.getValue(0));
|
|---|
| 2264 | + }
|
|---|
| 2265 | +
|
|---|
| 2266 | + @Test
|
|---|
| 2267 | + void testNumberGrouping() {
|
|---|
| 2268 | + // This is the float we are adding
|
|---|
| 2269 | + // 49 74 24 00 == 1_000_000f
|
|---|
| 2270 | + // 3f 80 00 00 == 1f
|
|---|
| 2271 | + byte[] newBytes = new byte[] {0x22, 0x09, 0x15, 0x00, 0x24, 0x74, 0x49};
|
|---|
| 2272 | + byte[] copyBytes = Arrays.copyOf(getSimpleFeatureLayerBytes(), getSimpleFeatureLayerBytes().length + newBytes.length - 4);
|
|---|
| 2273 | + // Change last few bytes
|
|---|
| 2274 | + System.arraycopy(newBytes, 0, copyBytes, 25, newBytes.length);
|
|---|
| 2275 | + // Update the length of the record
|
|---|
| 2276 | + copyBytes[1] = (byte) (copyBytes[1] + newBytes.length - 4);
|
|---|
| 2277 | + final NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
|---|
| 2278 | + final boolean numberFormatGroupingUsed = numberFormat.isGroupingUsed();
|
|---|
| 2279 | + // Sanity check
|
|---|
| 2280 | + Layer layer;
|
|---|
| 2281 | + try {
|
|---|
| 2282 | + numberFormat.setGroupingUsed(true);
|
|---|
| 2283 | + layer = assertDoesNotThrow(() -> getLayer(copyBytes));
|
|---|
| 2284 | + assertTrue(numberFormat.isGroupingUsed());
|
|---|
| 2285 | + } finally {
|
|---|
| 2286 | + numberFormat.setGroupingUsed(numberFormatGroupingUsed);
|
|---|
| 2287 | + }
|
|---|
| 2288 | + assertEquals(1, layer.getFeatures().size());
|
|---|
| 2289 | + assertEquals("t", layer.getName());
|
|---|
| 2290 | + assertEquals(2, layer.getVersion());
|
|---|
| 2291 | + assertEquals("a", layer.getKey(0));
|
|---|
| 2292 | + assertEquals(1_000_000f, ((Number) layer.getValue(0)).floatValue(), 0.00001);
|
|---|
| 2293 | +
|
|---|
| 2294 | + // Feature check
|
|---|
| 2295 | + Feature feature = layer.getFeatures().iterator().next();
|
|---|
| 2296 | + checkDefaultGeometry(feature);
|
|---|
| 2297 | + assertEquals("1000000", feature.getTags().get("a"));
|
|---|
| 2298 | + }
|
|---|
| 2299 | +
|
|---|
| 2300 | + private void testCreation(byte[] bytes) {
|
|---|
| 2301 | + Layer layer = assertDoesNotThrow(() -> getLayer(bytes));
|
|---|
| 2302 | + // Sanity check the layer
|
|---|
| 2303 | + assertEquals(1, layer.getFeatures().size());
|
|---|
| 2304 | + assertEquals("t", layer.getName());
|
|---|
| 2305 | + assertEquals(2, layer.getVersion());
|
|---|
| 2306 | + assertEquals("a", layer.getKey(0));
|
|---|
| 2307 | + assertSame(Boolean.TRUE, layer.getValue(0));
|
|---|
| 2308 | +
|
|---|
| 2309 | + // OK. Get the feature.
|
|---|
| 2310 | + Feature feature = layer.getFeatures().iterator().next();
|
|---|
| 2311 | +
|
|---|
| 2312 | + checkDefaultTags(feature);
|
|---|
| 2313 | +
|
|---|
| 2314 | + // Check id (should be the default of 0)
|
|---|
| 2315 | + assertEquals(1, feature.getId());
|
|---|
| 2316 | +
|
|---|
| 2317 | + checkDefaultGeometry(feature);
|
|---|
| 2318 | + }
|
|---|
| 2319 | +
|
|---|
| 2320 | + private void checkDefaultTags(Feature feature) {
|
|---|
| 2321 | + // Check tags
|
|---|
| 2322 | + assertEquals(1, feature.getTags().size());
|
|---|
| 2323 | + assertTrue(feature.getTags().containsKey("a"));
|
|---|
| 2324 | + // We are converting to a tag map (Map<String, String>), so "true"
|
|---|
| 2325 | + assertEquals("true", feature.getTags().get("a"));
|
|---|
| 2326 | + }
|
|---|
| 2327 | +
|
|---|
| 2328 | + private void checkDefaultGeometry(Feature feature) {
|
|---|
| 2329 | + // Check the geometry
|
|---|
| 2330 | + assertEquals(GeometryTypes.POINT, feature.getGeometryType());
|
|---|
| 2331 | + assertEquals(1, feature.getGeometry().size());
|
|---|
| 2332 | + CommandInteger geometry = feature.getGeometry().get(0);
|
|---|
| 2333 | + assertEquals(Command.MoveTo, geometry.getType());
|
|---|
| 2334 | + assertEquals(2, geometry.getOperations().length);
|
|---|
| 2335 | + assertEquals(25, geometry.getOperations()[0]);
|
|---|
| 2336 | + assertEquals(17, geometry.getOperations()[1]);
|
|---|
| 2337 | + assertNotNull(feature.getGeometryObject());
|
|---|
| 2338 | + assertEquals(feature.getGeometryObject(), feature.getGeometryObject());
|
|---|
| 2339 | + }
|
|---|
| 2340 | +}
|
|---|
| 2341 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTest.java
|
|---|
| 2342 | new file mode 100644
|
|---|
| 2343 | index 000000000..175d64cd7
|
|---|
| 2344 | --- /dev/null
|
|---|
| 2345 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTest.java
|
|---|
| 2346 | @@ -0,0 +1,169 @@
|
|---|
| 2347 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2348 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 2349 | +
|
|---|
| 2350 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 2351 | +import static org.junit.jupiter.api.Assertions.assertFalse;
|
|---|
| 2352 | +import static org.junit.jupiter.api.Assertions.assertThrows;
|
|---|
| 2353 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 2354 | +
|
|---|
| 2355 | +
|
|---|
| 2356 | +import java.awt.geom.Area;
|
|---|
| 2357 | +import java.awt.geom.Ellipse2D;
|
|---|
| 2358 | +import java.awt.geom.Path2D;
|
|---|
| 2359 | +import java.awt.geom.PathIterator;
|
|---|
| 2360 | +import java.awt.geom.Point2D;
|
|---|
| 2361 | +import java.util.ArrayList;
|
|---|
| 2362 | +import java.util.Arrays;
|
|---|
| 2363 | +import java.util.Collections;
|
|---|
| 2364 | +import java.util.List;
|
|---|
| 2365 | +
|
|---|
| 2366 | +import org.junit.jupiter.api.Test;
|
|---|
| 2367 | +
|
|---|
| 2368 | +/**
|
|---|
| 2369 | + * Test class for {@link Geometry}
|
|---|
| 2370 | + * @author Taylor Smock
|
|---|
| 2371 | + * @since xxx
|
|---|
| 2372 | + */
|
|---|
| 2373 | +class GeometryTest {
|
|---|
| 2374 | + /**
|
|---|
| 2375 | + * Create a command integer fairly easily
|
|---|
| 2376 | + * @param command The command type (see {@link Command})
|
|---|
| 2377 | + * @param parameters The parameters for the command
|
|---|
| 2378 | + * @return A command integer
|
|---|
| 2379 | + */
|
|---|
| 2380 | + private static CommandInteger createCommandInteger(int command, int... parameters) {
|
|---|
| 2381 | + CommandInteger commandInteger = new CommandInteger(command);
|
|---|
| 2382 | + if (parameters != null) {
|
|---|
| 2383 | + for (int parameter : parameters) {
|
|---|
| 2384 | + commandInteger.addParameter(parameter);
|
|---|
| 2385 | + }
|
|---|
| 2386 | + }
|
|---|
| 2387 | + return commandInteger;
|
|---|
| 2388 | + }
|
|---|
| 2389 | +
|
|---|
| 2390 | + /**
|
|---|
| 2391 | + * Check the current
|
|---|
| 2392 | + * @param pathIterator The path to check
|
|---|
| 2393 | + * @param expected The expected coords
|
|---|
| 2394 | + */
|
|---|
| 2395 | + private static void checkCurrentSegmentAndIncrement(PathIterator pathIterator, float... expected) {
|
|---|
| 2396 | + float[] coords = new float[6];
|
|---|
| 2397 | + int type = pathIterator.currentSegment(coords);
|
|---|
| 2398 | + pathIterator.next();
|
|---|
| 2399 | + for (int i = 0; i < expected.length; i++) {
|
|---|
| 2400 | + assertEquals(expected[i], coords[i]);
|
|---|
| 2401 | + }
|
|---|
| 2402 | + if (Arrays.asList(PathIterator.SEG_MOVETO, PathIterator.SEG_LINETO).contains(type)) {
|
|---|
| 2403 | + assertEquals(2, expected.length, "You should check both x and y coordinates");
|
|---|
| 2404 | + } else if (PathIterator.SEG_QUADTO == type) {
|
|---|
| 2405 | + assertEquals(4, expected.length, "You should check all x and y coordinates");
|
|---|
| 2406 | + } else if (PathIterator.SEG_CUBICTO == type) {
|
|---|
| 2407 | + assertEquals(6, expected.length, "You should check all x and y coordinates");
|
|---|
| 2408 | + } else if (PathIterator.SEG_CLOSE == type) {
|
|---|
| 2409 | + assertEquals(0, expected.length, "CloseTo has no expected coordinates to check");
|
|---|
| 2410 | + }
|
|---|
| 2411 | + }
|
|---|
| 2412 | +
|
|---|
| 2413 | + @Test
|
|---|
| 2414 | + void testBadGeometry() {
|
|---|
| 2415 | + IllegalArgumentException badPointException = assertThrows(IllegalArgumentException.class,
|
|---|
| 2416 | + () -> new Geometry(GeometryTypes.POINT, Collections.singletonList(createCommandInteger(1))));
|
|---|
| 2417 | + assertEquals("POINT with 0 arguments is not understood", badPointException.getMessage());
|
|---|
| 2418 | + IllegalArgumentException badLineException = assertThrows(IllegalArgumentException.class,
|
|---|
| 2419 | + () -> new Geometry(GeometryTypes.LINESTRING, Collections.singletonList(createCommandInteger(15))));
|
|---|
| 2420 | + assertEquals("LINESTRING with 0 arguments is not understood", badLineException.getMessage());
|
|---|
| 2421 | + }
|
|---|
| 2422 | +
|
|---|
| 2423 | + @Test
|
|---|
| 2424 | + void testPoint() {
|
|---|
| 2425 | + CommandInteger moveTo = createCommandInteger(9, 17, 34);
|
|---|
| 2426 | + Geometry geometry = new Geometry(GeometryTypes.POINT, Collections.singletonList(moveTo));
|
|---|
| 2427 | + assertEquals(1, geometry.getShapes().size());
|
|---|
| 2428 | + Ellipse2D shape = (Ellipse2D) geometry.getShapes().iterator().next();
|
|---|
| 2429 | + assertEquals(17, shape.getCenterX());
|
|---|
| 2430 | + assertEquals(34, shape.getCenterY());
|
|---|
| 2431 | + }
|
|---|
| 2432 | +
|
|---|
| 2433 | + @Test
|
|---|
| 2434 | + void testLine() {
|
|---|
| 2435 | + CommandInteger moveTo = createCommandInteger(9, 2, 2);
|
|---|
| 2436 | + CommandInteger lineTo = createCommandInteger(18, 0, 8, 8, 0);
|
|---|
| 2437 | + Geometry geometry = new Geometry(GeometryTypes.LINESTRING, Arrays.asList(moveTo, lineTo));
|
|---|
| 2438 | + assertEquals(1, geometry.getShapes().size());
|
|---|
| 2439 | + Path2D path = (Path2D) geometry.getShapes().iterator().next();
|
|---|
| 2440 | + PathIterator pathIterator = path.getPathIterator(null);
|
|---|
| 2441 | + checkCurrentSegmentAndIncrement(pathIterator, 2, 2);
|
|---|
| 2442 | + checkCurrentSegmentAndIncrement(pathIterator, 2, 10);
|
|---|
| 2443 | + checkCurrentSegmentAndIncrement(pathIterator, 10, 10);
|
|---|
| 2444 | + assertTrue(pathIterator.isDone());
|
|---|
| 2445 | + }
|
|---|
| 2446 | +
|
|---|
| 2447 | + @Test
|
|---|
| 2448 | + void testPolygon() {
|
|---|
| 2449 | + List<CommandInteger> commands = new ArrayList<>(3);
|
|---|
| 2450 | + commands.add(createCommandInteger(9, 3, 6));
|
|---|
| 2451 | + commands.add(createCommandInteger(18, 5, 6, 12, 22));
|
|---|
| 2452 | + commands.add(createCommandInteger(15));
|
|---|
| 2453 | +
|
|---|
| 2454 | + Geometry geometry = new Geometry(GeometryTypes.POLYGON, commands);
|
|---|
| 2455 | + assertEquals(1, geometry.getShapes().size());
|
|---|
| 2456 | +
|
|---|
| 2457 | + Area area = (Area) geometry.getShapes().iterator().next();
|
|---|
| 2458 | + PathIterator pathIterator = area.getPathIterator(null);
|
|---|
| 2459 | + checkCurrentSegmentAndIncrement(pathIterator, 3, 6);
|
|---|
| 2460 | + // This is somewhat unexpected, and may change based off of JVM implementations
|
|---|
| 2461 | + // But for whatever reason, Java flips the inner coordinates in this case.
|
|---|
| 2462 | + checkCurrentSegmentAndIncrement(pathIterator, 20, 34);
|
|---|
| 2463 | + checkCurrentSegmentAndIncrement(pathIterator, 8, 12);
|
|---|
| 2464 | + checkCurrentSegmentAndIncrement(pathIterator, 3, 6);
|
|---|
| 2465 | + checkCurrentSegmentAndIncrement(pathIterator);
|
|---|
| 2466 | + assertTrue(pathIterator.isDone());
|
|---|
| 2467 | + }
|
|---|
| 2468 | +
|
|---|
| 2469 | + @Test
|
|---|
| 2470 | + void testBadPolygon() {
|
|---|
| 2471 | + /*
|
|---|
| 2472 | + * "Linear rings MUST be geometric objects that have no anomalous geometric points,
|
|---|
| 2473 | + * such as self-intersection or self-tangency. The position of the cursor before
|
|---|
| 2474 | + * calling the ClosePath command of a linear ring SHALL NOT repeat the same position
|
|---|
| 2475 | + * as the first point in the linear ring as this would create a zero-length line
|
|---|
| 2476 | + * segment. A linear ring SHOULD NOT have an area calculated by the surveyor's
|
|---|
| 2477 | + * formula equal to zero, as this would signify a ring with anomalous geometric points."
|
|---|
| 2478 | + */
|
|---|
| 2479 | + List<CommandInteger> commands = new ArrayList<>(3);
|
|---|
| 2480 | + commands.add(createCommandInteger(9, 0, 0));
|
|---|
| 2481 | + commands.add(createCommandInteger(18, 0, 0));
|
|---|
| 2482 | + commands.add(createCommandInteger(15));
|
|---|
| 2483 | + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> new Geometry(GeometryTypes.POLYGON, commands));
|
|---|
| 2484 | + assertEquals("POLYGON cannot have zero area", exception.getMessage());
|
|---|
| 2485 | + }
|
|---|
| 2486 | +
|
|---|
| 2487 | + @Test
|
|---|
| 2488 | + void testMultiPolygon() {
|
|---|
| 2489 | + List<CommandInteger> commands = new ArrayList<>(10);
|
|---|
| 2490 | + // Polygon 1
|
|---|
| 2491 | + commands.add(createCommandInteger(9, 0, 0));
|
|---|
| 2492 | + commands.add(createCommandInteger(26, 10, 0, 0, 10, -10, 0));
|
|---|
| 2493 | + commands.add(createCommandInteger(15));
|
|---|
| 2494 | + // Polygon 2 outer
|
|---|
| 2495 | + commands.add(createCommandInteger(9, 11, 1));
|
|---|
| 2496 | + commands.add(createCommandInteger(26, 9, 0, 0, 9, -9, 0));
|
|---|
| 2497 | + commands.add(createCommandInteger(15));
|
|---|
| 2498 | + // Polygon 2 inner
|
|---|
| 2499 | + commands.add(createCommandInteger(9, 2, -7));
|
|---|
| 2500 | + commands.add(createCommandInteger(26, 0, 4, 4, 0, 0, -4));
|
|---|
| 2501 | + commands.add(createCommandInteger(15));
|
|---|
| 2502 | +
|
|---|
| 2503 | + Geometry geometry = new Geometry(GeometryTypes.POLYGON, commands);
|
|---|
| 2504 | + assertEquals(1, geometry.getShapes().size());
|
|---|
| 2505 | + Area area = (Area) geometry.getShapes().iterator().next();
|
|---|
| 2506 | + assertFalse(area.isSingular());
|
|---|
| 2507 | + PathIterator pathIterator = area.getPathIterator(null);
|
|---|
| 2508 | + assertEquals(PathIterator.WIND_NON_ZERO, pathIterator.getWindingRule());
|
|---|
| 2509 | + assertTrue(area.contains(new Point2D.Float(5, 5)));
|
|---|
| 2510 | + assertTrue(area.contains(new Point2D.Float(12, 12)));
|
|---|
| 2511 | + assertFalse(area.contains(new Point2D.Float(15, 15)));
|
|---|
| 2512 | + assertFalse(area.contains(new Point2D.Float(10, 11)));
|
|---|
| 2513 | + assertFalse(area.contains(new Point2D.Float(-1, -1)));
|
|---|
| 2514 | + }
|
|---|
| 2515 | +}
|
|---|
| 2516 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypesTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypesTest.java
|
|---|
| 2517 | new file mode 100644
|
|---|
| 2518 | index 000000000..da0f9b3c7
|
|---|
| 2519 | --- /dev/null
|
|---|
| 2520 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/GeometryTypesTest.java
|
|---|
| 2521 | @@ -0,0 +1,47 @@
|
|---|
| 2522 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2523 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 2524 | +
|
|---|
| 2525 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 2526 | +import static org.junit.jupiter.api.Assertions.fail;
|
|---|
| 2527 | +
|
|---|
| 2528 | +
|
|---|
| 2529 | +import org.openstreetmap.josm.TestUtils;
|
|---|
| 2530 | +
|
|---|
| 2531 | +import org.junit.jupiter.api.Test;
|
|---|
| 2532 | +import org.junit.jupiter.params.ParameterizedTest;
|
|---|
| 2533 | +import org.junit.jupiter.params.provider.EnumSource;
|
|---|
| 2534 | +
|
|---|
| 2535 | +/**
|
|---|
| 2536 | + * Test class for {@link GeometryTypes}
|
|---|
| 2537 | + * @author Taylor Smock
|
|---|
| 2538 | + * @since xxx
|
|---|
| 2539 | + */
|
|---|
| 2540 | +class GeometryTypesTest {
|
|---|
| 2541 | + @Test
|
|---|
| 2542 | + void testNaiveEnumTest() {
|
|---|
| 2543 | + TestUtils.superficialEnumCodeCoverage(GeometryTypes.class);
|
|---|
| 2544 | + TestUtils.superficialEnumCodeCoverage(GeometryTypes.Ring.class);
|
|---|
| 2545 | + }
|
|---|
| 2546 | +
|
|---|
| 2547 | + @ParameterizedTest
|
|---|
| 2548 | + @EnumSource(GeometryTypes.class)
|
|---|
| 2549 | + void testExpectedIds(GeometryTypes type) {
|
|---|
| 2550 | + // Ensure that users can get the type from the ordinal
|
|---|
| 2551 | + // See https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L8
|
|---|
| 2552 | + // for the expected values
|
|---|
| 2553 | + final int expectedId;
|
|---|
| 2554 | + if (type == GeometryTypes.UNKNOWN) {
|
|---|
| 2555 | + expectedId = 0;
|
|---|
| 2556 | + } else if (type == GeometryTypes.POINT) {
|
|---|
| 2557 | + expectedId = 1;
|
|---|
| 2558 | + } else if (type == GeometryTypes.LINESTRING) {
|
|---|
| 2559 | + expectedId = 2;
|
|---|
| 2560 | + } else if (type == GeometryTypes.POLYGON) {
|
|---|
| 2561 | + expectedId = 3;
|
|---|
| 2562 | + } else {
|
|---|
| 2563 | + fail("Unknown geometry type, see vector tile spec");
|
|---|
| 2564 | + expectedId = Integer.MIN_VALUE;
|
|---|
| 2565 | + }
|
|---|
| 2566 | + assertEquals(expectedId, type.ordinal());
|
|---|
| 2567 | + }
|
|---|
| 2568 | +}
|
|---|
| 2569 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 2570 | new file mode 100644
|
|---|
| 2571 | index 000000000..fc3ba9c27
|
|---|
| 2572 | --- /dev/null
|
|---|
| 2573 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 2574 | @@ -0,0 +1,135 @@
|
|---|
| 2575 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2576 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 2577 | +
|
|---|
| 2578 | +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|---|
| 2579 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 2580 | +import static org.junit.jupiter.api.Assertions.assertThrows;
|
|---|
| 2581 | +
|
|---|
| 2582 | +import java.io.FileInputStream;
|
|---|
| 2583 | +import java.io.IOException;
|
|---|
| 2584 | +import java.util.Arrays;
|
|---|
| 2585 | +import java.util.List;
|
|---|
| 2586 | +
|
|---|
| 2587 | +import org.openstreetmap.josm.TestUtils;
|
|---|
| 2588 | +import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 2589 | +import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 2590 | +
|
|---|
| 2591 | +import nl.jqno.equalsverifier.EqualsVerifier;
|
|---|
| 2592 | +import org.junit.jupiter.api.Test;
|
|---|
| 2593 | +
|
|---|
| 2594 | +/**
|
|---|
| 2595 | + * Test class for {@link Layer}
|
|---|
| 2596 | + */
|
|---|
| 2597 | +public class LayerTest {
|
|---|
| 2598 | + /**
|
|---|
| 2599 | + * This looks something like this (if it were json). Note that some keys could be repeated,
|
|---|
| 2600 | + * and so could be better represented as an array. Specifically, "features", "key", and "value".
|
|---|
| 2601 | + * "layer": {
|
|---|
| 2602 | + * "name": "t",
|
|---|
| 2603 | + * "version": 2,
|
|---|
| 2604 | + * "features": {
|
|---|
| 2605 | + * "type": "POINT",
|
|---|
| 2606 | + * "tags": [0, 0],
|
|---|
| 2607 | + * "geometry": [9, 50, 34]
|
|---|
| 2608 | + * },
|
|---|
| 2609 | + * "key": "a",
|
|---|
| 2610 | + * "value": true
|
|---|
| 2611 | + * }
|
|---|
| 2612 | + *
|
|---|
| 2613 | + * WARNING: DO NOT MODIFY THIS ARRAY DIRECTLY -- it could contaminate other tests
|
|---|
| 2614 | + */
|
|---|
| 2615 | + private static final byte[] simpleFeatureLayerBytes = new byte[] {
|
|---|
| 2616 | + 0x1a, 0x1b, // layer, 27 bytes for the rest
|
|---|
| 2617 | + 0x0a, 0x01, 0x74, // name=t
|
|---|
| 2618 | + 0x78, 0x02, // version=2
|
|---|
| 2619 | + 0x12, 0x0d, // features, 11 bytes
|
|---|
| 2620 | + 0x08, 0x01, // id=1
|
|---|
| 2621 | + 0x18, 0x01, // type=POINT
|
|---|
| 2622 | + 0x12, 0x02, 0x00, 0x00, // tags=[0, 0] (packed). Non-packed would be [0x10, 0x00, 0x10, 0x00]
|
|---|
| 2623 | + 0x22, 0x03, 0x09, 0x32, 0x22, // geometry=[9, 50, 34]
|
|---|
| 2624 | + 0x1a, 0x01, 0x61, // key=a
|
|---|
| 2625 | + 0x22, 0x02, 0x38, 0x01, // value=true (boolean)
|
|---|
| 2626 | + };
|
|---|
| 2627 | +
|
|---|
| 2628 | + /**
|
|---|
| 2629 | + * Gets a copy of {@link #simpleFeatureLayerBytes} so that a test doesn't accidentally change the bytes
|
|---|
| 2630 | + * @return An array that can be modified.
|
|---|
| 2631 | + */
|
|---|
| 2632 | + static byte[] getSimpleFeatureLayerBytes() {
|
|---|
| 2633 | + return Arrays.copyOf(simpleFeatureLayerBytes, simpleFeatureLayerBytes.length);
|
|---|
| 2634 | + }
|
|---|
| 2635 | +
|
|---|
| 2636 | + /**
|
|---|
| 2637 | + * Create a layer from bytes
|
|---|
| 2638 | + * @param bytes The bytes that make up the layer
|
|---|
| 2639 | + * @return The generated layer
|
|---|
| 2640 | + * @throws IOException If something happened (should never trigger)
|
|---|
| 2641 | + */
|
|---|
| 2642 | + static Layer getLayer(byte[] bytes) throws IOException {
|
|---|
| 2643 | + List<ProtoBufRecord> records = (List<ProtoBufRecord>) new ProtoBufParser(bytes).allRecords();
|
|---|
| 2644 | + assertEquals(1, records.size());
|
|---|
| 2645 | + return new Layer(new ProtoBufParser(records.get(0).getBytes()).allRecords());
|
|---|
| 2646 | + }
|
|---|
| 2647 | +
|
|---|
| 2648 | + @Test
|
|---|
| 2649 | + void testLayerCreation() throws IOException {
|
|---|
| 2650 | + List<ProtoBufRecord> layers = (List<ProtoBufRecord>) new ProtoBufParser(new FileInputStream(TestUtils.getTestDataRoot()
|
|---|
| 2651 | + + "pbf/mapillary/14/3249/6258.mvt")).allRecords();
|
|---|
| 2652 | + Layer sequenceLayer = new Layer(layers.get(0).getBytes());
|
|---|
| 2653 | + assertEquals("mapillary-sequences", sequenceLayer.getName());
|
|---|
| 2654 | + assertEquals(1, sequenceLayer.getFeatures().size());
|
|---|
| 2655 | + assertEquals(1, sequenceLayer.getGeometry().size());
|
|---|
| 2656 | + assertEquals(2048, sequenceLayer.getExtent());
|
|---|
| 2657 | + assertEquals(1, sequenceLayer.getVersion());
|
|---|
| 2658 | +
|
|---|
| 2659 | + Layer imageLayer = new Layer(layers.get(1).getBytes());
|
|---|
| 2660 | + assertEquals("mapillary-images", imageLayer.getName());
|
|---|
| 2661 | + assertEquals(116, imageLayer.getFeatures().size());
|
|---|
| 2662 | + assertEquals(116, imageLayer.getGeometry().size());
|
|---|
| 2663 | + assertEquals(2048, imageLayer.getExtent());
|
|---|
| 2664 | + assertEquals(1, imageLayer.getVersion());
|
|---|
| 2665 | + }
|
|---|
| 2666 | +
|
|---|
| 2667 | + @Test
|
|---|
| 2668 | + void testLayerEqualsHashCode() throws IOException {
|
|---|
| 2669 | + List<ProtoBufRecord> layers = (List<ProtoBufRecord>) new ProtoBufParser(new FileInputStream(TestUtils.getTestDataRoot()
|
|---|
| 2670 | + + "pbf/mapillary/14/3249/6258.mvt")).allRecords();
|
|---|
| 2671 | + EqualsVerifier.forClass(Layer.class).withPrefabValues(byte[].class, layers.get(0).getBytes(), layers.get(1).getBytes())
|
|---|
| 2672 | + .verify();
|
|---|
| 2673 | + }
|
|---|
| 2674 | +
|
|---|
| 2675 | + @Test
|
|---|
| 2676 | + void testVersionsNumbers() {
|
|---|
| 2677 | + byte[] copyByte = getSimpleFeatureLayerBytes();
|
|---|
| 2678 | + assertEquals(2, assertDoesNotThrow(() -> getLayer(copyByte)).getVersion());
|
|---|
| 2679 | + copyByte[6] = 1;
|
|---|
| 2680 | + assertEquals(1, assertDoesNotThrow(() -> getLayer(copyByte)).getVersion());
|
|---|
| 2681 | + copyByte[6] = 0;
|
|---|
| 2682 | + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> getLayer(copyByte));
|
|---|
| 2683 | + assertEquals("We do not understand version 0 of the vector tile specification", exception.getMessage());
|
|---|
| 2684 | + copyByte[6] = 3;
|
|---|
| 2685 | + exception = assertThrows(IllegalArgumentException.class, () -> getLayer(copyByte));
|
|---|
| 2686 | + assertEquals("We do not understand version 3 of the vector tile specification", exception.getMessage());
|
|---|
| 2687 | + // Remove version number (AKA change it to some unknown field). Default is version=1.
|
|---|
| 2688 | + copyByte[5] = 0x18;
|
|---|
| 2689 | + assertEquals(1, assertDoesNotThrow(() -> getLayer(copyByte)).getVersion());
|
|---|
| 2690 | + }
|
|---|
| 2691 | +
|
|---|
| 2692 | + @Test
|
|---|
| 2693 | + void testLayerName() throws IOException {
|
|---|
| 2694 | + byte[] copyByte = getSimpleFeatureLayerBytes();
|
|---|
| 2695 | + Layer layer = getLayer(copyByte);
|
|---|
| 2696 | + assertEquals("t", layer.getName());
|
|---|
| 2697 | + copyByte[2] = 0x1a; // name=t -> ?
|
|---|
| 2698 | + Exception noNameException = assertThrows(IllegalArgumentException.class, () -> getLayer(copyByte));
|
|---|
| 2699 | + assertEquals("Vector tile layers must have a layer name", noNameException.getMessage());
|
|---|
| 2700 | + }
|
|---|
| 2701 | +
|
|---|
| 2702 | + @Test
|
|---|
| 2703 | + void testUnknownField() {
|
|---|
| 2704 | + byte[] copyByte = getSimpleFeatureLayerBytes();
|
|---|
| 2705 | + copyByte[27] = 0x78;
|
|---|
| 2706 | + Exception unknownField = assertThrows(IllegalArgumentException.class, () -> getLayer(copyByte));
|
|---|
| 2707 | + assertEquals("Unknown field in vector tile layer value (15)", unknownField.getMessage());
|
|---|
| 2708 | + }
|
|---|
| 2709 | +}
|
|---|
| 2710 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 2711 | new file mode 100644
|
|---|
| 2712 | index 000000000..66e4ea781
|
|---|
| 2713 | --- /dev/null
|
|---|
| 2714 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 2715 | @@ -0,0 +1,82 @@
|
|---|
| 2716 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2717 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 2718 | +
|
|---|
| 2719 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 2720 | +import static org.junit.jupiter.api.Assertions.assertNull;
|
|---|
| 2721 | +
|
|---|
| 2722 | +import java.awt.image.BufferedImage;
|
|---|
| 2723 | +import java.util.Collections;
|
|---|
| 2724 | +import java.util.stream.Stream;
|
|---|
| 2725 | +
|
|---|
| 2726 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 2727 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
|
|---|
| 2728 | +import org.openstreetmap.josm.TestUtils;
|
|---|
| 2729 | +import org.openstreetmap.josm.data.cache.JCSCacheManager;
|
|---|
| 2730 | +import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 2731 | +import org.openstreetmap.josm.data.imagery.TileJobOptions;
|
|---|
| 2732 | +import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 2733 | +
|
|---|
| 2734 | +import org.awaitility.Awaitility;
|
|---|
| 2735 | +import org.awaitility.Durations;
|
|---|
| 2736 | +import org.junit.jupiter.api.BeforeEach;
|
|---|
| 2737 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 2738 | +import org.junit.jupiter.params.ParameterizedTest;
|
|---|
| 2739 | +import org.junit.jupiter.params.provider.Arguments;
|
|---|
| 2740 | +import org.junit.jupiter.params.provider.MethodSource;
|
|---|
| 2741 | +
|
|---|
| 2742 | +/**
|
|---|
| 2743 | + * Test class for {@link MVTTile}
|
|---|
| 2744 | + */
|
|---|
| 2745 | +public class MVTTileTest {
|
|---|
| 2746 | + private MapboxVectorTileSource tileSource;
|
|---|
| 2747 | + private MapBoxVectorCachedTileLoader loader;
|
|---|
| 2748 | + @RegisterExtension
|
|---|
| 2749 | + JOSMTestRules rule = new JOSMTestRules();
|
|---|
| 2750 | + @BeforeEach
|
|---|
| 2751 | + void setup() {
|
|---|
| 2752 | + tileSource = new MapboxVectorTileSource(new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot()
|
|---|
| 2753 | + + "pbf/mapillary/{z}/{x}/{y}.mvt"));
|
|---|
| 2754 | + loader = new MapBoxVectorCachedTileLoader(null,
|
|---|
| 2755 | + JCSCacheManager.getCache("testMapillaryCache"), new TileJobOptions(1, 1, Collections
|
|---|
| 2756 | + .emptyMap(), 3600));
|
|---|
| 2757 | + }
|
|---|
| 2758 | +
|
|---|
| 2759 | + /**
|
|---|
| 2760 | + * Provide arguments for {@link #testMVTTile(BufferedImage, Boolean)}
|
|---|
| 2761 | + * @return The arguments to use
|
|---|
| 2762 | + */
|
|---|
| 2763 | + private static Stream<Arguments> testMVTTile() {
|
|---|
| 2764 | + return Stream.of(
|
|---|
| 2765 | + Arguments.of(null, Boolean.TRUE),
|
|---|
| 2766 | + Arguments.of(Tile.LOADING_IMAGE, Boolean.TRUE),
|
|---|
| 2767 | + Arguments.of(Tile.ERROR_IMAGE, Boolean.TRUE),
|
|---|
| 2768 | + Arguments.of(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), Boolean.FALSE)
|
|---|
| 2769 | + );
|
|---|
| 2770 | + }
|
|---|
| 2771 | +
|
|---|
| 2772 | + @ParameterizedTest
|
|---|
| 2773 | + @MethodSource("testMVTTile")
|
|---|
| 2774 | + void testMVTTile(BufferedImage image, Boolean isLoaded) {
|
|---|
| 2775 | + MVTTile tile = new MVTTile(tileSource, 3249, 6258, 14);
|
|---|
| 2776 | + tile.setImage(image);
|
|---|
| 2777 | + assertEquals(image, tile.getImage());
|
|---|
| 2778 | +
|
|---|
| 2779 | + TileJob job = loader.createTileLoaderJob(tile);
|
|---|
| 2780 | + job.submit();
|
|---|
| 2781 | + Awaitility.await().atMost(Durations.ONE_SECOND).until(tile::isLoaded);
|
|---|
| 2782 | + if (isLoaded) {
|
|---|
| 2783 | + Awaitility.await().atMost(Durations.ONE_SECOND).until(() -> tile.getLayers() != null && tile.getLayers().size() > 1);
|
|---|
| 2784 | + assertEquals(2, tile.getLayers().size());
|
|---|
| 2785 | + // The test Mapillary tiles have 2048 instead of 4096 for their extent. This *may* change
|
|---|
| 2786 | + // in future Mapillary tiles, so if the test PBF files are updated, beware.
|
|---|
| 2787 | + assertEquals(2048, tile.getExtent());
|
|---|
| 2788 | + // Ensure that we have the clear image set, such that the tile doesn't add to the dataset again
|
|---|
| 2789 | + // and we don't have a loading image
|
|---|
| 2790 | + assertEquals(MVTTile.CLEAR_LOADED, tile.getImage());
|
|---|
| 2791 | + } else {
|
|---|
| 2792 | + assertNull(tile.getLayers());
|
|---|
| 2793 | + assertEquals(image, tile.getImage());
|
|---|
| 2794 | + }
|
|---|
| 2795 | + }
|
|---|
| 2796 | +
|
|---|
| 2797 | +}
|
|---|
| 2798 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java
|
|---|
| 2799 | new file mode 100644
|
|---|
| 2800 | index 000000000..5b9f16842
|
|---|
| 2801 | --- /dev/null
|
|---|
| 2802 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java
|
|---|
| 2803 | @@ -0,0 +1,77 @@
|
|---|
| 2804 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2805 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 2806 | +
|
|---|
| 2807 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 2808 | +import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|---|
| 2809 | +import static org.junit.jupiter.api.Assertions.assertNull;
|
|---|
| 2810 | +
|
|---|
| 2811 | +
|
|---|
| 2812 | +import java.util.stream.Stream;
|
|---|
| 2813 | +
|
|---|
| 2814 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 2815 | +import org.openstreetmap.josm.TestUtils;
|
|---|
| 2816 | +import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 2817 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapBoxVectorStyle;
|
|---|
| 2818 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.Source;
|
|---|
| 2819 | +import org.openstreetmap.josm.gui.ExtendedDialog;
|
|---|
| 2820 | +import org.openstreetmap.josm.gui.widgets.JosmComboBox;
|
|---|
| 2821 | +import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 2822 | +import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker;
|
|---|
| 2823 | +
|
|---|
| 2824 | +import org.junit.jupiter.api.Test;
|
|---|
| 2825 | +import org.junit.jupiter.params.ParameterizedTest;
|
|---|
| 2826 | +import org.junit.jupiter.params.provider.Arguments;
|
|---|
| 2827 | +import org.junit.jupiter.params.provider.MethodSource;
|
|---|
| 2828 | +
|
|---|
| 2829 | +/**
|
|---|
| 2830 | + * Test class for {@link MapboxVectorTileSource}
|
|---|
| 2831 | + * @author Taylor Smock
|
|---|
| 2832 | + * @since xxx
|
|---|
| 2833 | + */
|
|---|
| 2834 | +class MapboxVectorTileSourceTest {
|
|---|
| 2835 | + @RegisterExtension
|
|---|
| 2836 | + JOSMTestRules rule = new JOSMTestRules();
|
|---|
| 2837 | + private static class SelectLayerDialogMocker extends ExtendedDialogMocker {
|
|---|
| 2838 | + int index;
|
|---|
| 2839 | + @Override
|
|---|
| 2840 | + protected void act(final ExtendedDialog instance) {
|
|---|
| 2841 | + ((JosmComboBox<?>) this.getContent(instance)).setSelectedIndex(index);
|
|---|
| 2842 | + }
|
|---|
| 2843 | +
|
|---|
| 2844 | + @Override
|
|---|
| 2845 | + protected String getString(final ExtendedDialog instance) {
|
|---|
| 2846 | + return String.join(";", ((Source) ((JosmComboBox<?>) this.getContent(instance)).getSelectedItem()).getUrls());
|
|---|
| 2847 | + }
|
|---|
| 2848 | + }
|
|---|
| 2849 | +
|
|---|
| 2850 | + @Test
|
|---|
| 2851 | + void testNoStyle() {
|
|---|
| 2852 | + MapboxVectorTileSource tileSource = new MapboxVectorTileSource(
|
|---|
| 2853 | + new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot() + "pbf/mapillary/{z}/{x}/{y}.mvt"));
|
|---|
| 2854 | + assertNull(tileSource.getStyleSource());
|
|---|
| 2855 | + }
|
|---|
| 2856 | +
|
|---|
| 2857 | + private static Stream<Arguments> testMapillaryStyle() {
|
|---|
| 2858 | + return Stream.of(Arguments.of(0, "Test Mapillary: mapillary-source", "https://tiles3.mapillary.com/v0.1/{z}/{x}/{y}.mvt"),
|
|---|
| 2859 | + Arguments.of(1, "Test Mapillary: mapillary-features-source",
|
|---|
| 2860 | + "https://a.mapillary.com/v3/map_features?tile={z}/{x}/{y}&client_id=_apiKey_"
|
|---|
| 2861 | + + "&layers=points&per_page=1000"),
|
|---|
| 2862 | + Arguments.of(2, "Test Mapillary: mapillary-traffic-signs-source",
|
|---|
| 2863 | + "https://a.mapillary.com/v3/map_features?tile={z}/{x}/{y}&client_id=_apiKey_"
|
|---|
| 2864 | + + "&layers=trafficsigns&per_page=1000"));
|
|---|
| 2865 | + }
|
|---|
| 2866 | +
|
|---|
| 2867 | + @ParameterizedTest
|
|---|
| 2868 | + @MethodSource("testMapillaryStyle")
|
|---|
| 2869 | + void testMapillaryStyle(Integer index, String expected, String dialogMockerText) {
|
|---|
| 2870 | + TestUtils.assumeWorkingJMockit();
|
|---|
| 2871 | + SelectLayerDialogMocker extendedDialogMocker = new SelectLayerDialogMocker();
|
|---|
| 2872 | + extendedDialogMocker.index = index;
|
|---|
| 2873 | + extendedDialogMocker.getMockResultMap().put(dialogMockerText, "Add layers");
|
|---|
| 2874 | + MapboxVectorTileSource tileSource = new MapboxVectorTileSource(
|
|---|
| 2875 | + new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot() + "mapillary.json"));
|
|---|
| 2876 | + MapBoxVectorStyle styleSource = tileSource.getStyleSource();
|
|---|
| 2877 | + assertNotNull(styleSource);
|
|---|
| 2878 | + assertEquals(expected, tileSource.toString());
|
|---|
| 2879 | + }
|
|---|
| 2880 | +}
|
|---|
| 2881 | --
|
|---|
| 2882 | GitLab
|
|---|
| 2883 |
|
|---|
| 2884 |
|
|---|
| 2885 | From 391b4caff31a6e8b87be6d4c256ac2303315e36a Mon Sep 17 00:00:00 2001
|
|---|
| 2886 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 2887 | Date: Mon, 19 Apr 2021 16:09:36 -0600
|
|---|
| 2888 | Subject: [PATCH 03/50] Layer: FIXUP: extent is uint not sint
|
|---|
| 2889 |
|
|---|
| 2890 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 2891 | ---
|
|---|
| 2892 | .../josm/data/imagery/vectortile/mapbox/Layer.java | 2 +-
|
|---|
| 2893 | 1 file changed, 1 insertion(+), 1 deletion(-)
|
|---|
| 2894 |
|
|---|
| 2895 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 2896 | index 09851e8c7..1c496d55d 100644
|
|---|
| 2897 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 2898 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 2899 | @@ -107,7 +107,7 @@ public final class Layer {
|
|---|
| 2900 | }
|
|---|
| 2901 | this.name = sorted.getOrDefault((int) NAME_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asString).findFirst()
|
|---|
| 2902 | .orElseThrow(() -> new IllegalArgumentException(tr("Vector tile layers must have a layer name")));
|
|---|
| 2903 | - this.extent = sorted.getOrDefault((int) EXTENT_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asSignedVarInt)
|
|---|
| 2904 | + this.extent = sorted.getOrDefault((int) EXTENT_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asUnsignedVarInt)
|
|---|
| 2905 | .map(Number::intValue).findAny().orElse(DEFAULT_EXTENT);
|
|---|
| 2906 |
|
|---|
| 2907 | sorted.getOrDefault((int) KEY_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asString)
|
|---|
| 2908 | --
|
|---|
| 2909 | GitLab
|
|---|
| 2910 |
|
|---|
| 2911 |
|
|---|
| 2912 | From cd40f9014f178fd97f2679656cc328e73998f26f Mon Sep 17 00:00:00 2001
|
|---|
| 2913 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 2914 | Date: Thu, 8 Apr 2021 14:25:43 -0600
|
|---|
| 2915 | Subject: [PATCH 04/50] Initial Mapbox Vector Style implementation
|
|---|
| 2916 |
|
|---|
| 2917 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 2918 | ---
|
|---|
| 2919 | .../vectortile/mapbox/style/Expression.java | 99 +++
|
|---|
| 2920 | .../vectortile/mapbox/style/Layers.java | 519 +++++++++++++++
|
|---|
| 2921 | .../mapbox/style/MapBoxVectorStyle.java | 266 ++++++++
|
|---|
| 2922 | .../vectortile/mapbox/style/Scheme.java | 12 +
|
|---|
| 2923 | .../vectortile/mapbox/style/Source.java | 254 ++++++++
|
|---|
| 2924 | .../vectortile/mapbox/style/SourceType.java | 17 +
|
|---|
| 2925 | .../josm/data/osm/IPrimitive.java | 9 +
|
|---|
| 2926 | .../josm/gui/mappaint/ElemStyles.java | 118 ++--
|
|---|
| 2927 | .../mapbox/style/ExpressionTest.java | 53 ++
|
|---|
| 2928 | .../vectortile/mapbox/style/LayersTest.java | 601 ++++++++++++++++++
|
|---|
| 2929 | .../mapbox/style/MapBoxVectorStyleTest.java | 300 +++++++++
|
|---|
| 2930 | .../vectortile/mapbox/style/SourceTest.java | 188 ++++++
|
|---|
| 2931 | 12 files changed, 2383 insertions(+), 53 deletions(-)
|
|---|
| 2932 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java
|
|---|
| 2933 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 2934 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 2935 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Scheme.java
|
|---|
| 2936 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 2937 | create mode 100644 src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java
|
|---|
| 2938 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/ExpressionTest.java
|
|---|
| 2939 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 2940 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyleTest.java
|
|---|
| 2941 | create mode 100644 test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java
|
|---|
| 2942 |
|
|---|
| 2943 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java
|
|---|
| 2944 | new file mode 100644
|
|---|
| 2945 | index 000000000..a7f677755
|
|---|
| 2946 | --- /dev/null
|
|---|
| 2947 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java
|
|---|
| 2948 | @@ -0,0 +1,99 @@
|
|---|
| 2949 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 2950 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 2951 | +
|
|---|
| 2952 | +import java.util.Arrays;
|
|---|
| 2953 | +import java.util.Objects;
|
|---|
| 2954 | +import java.util.stream.Collectors;
|
|---|
| 2955 | +
|
|---|
| 2956 | +import javax.json.JsonArray;
|
|---|
| 2957 | +import javax.json.JsonObject;
|
|---|
| 2958 | +import javax.json.JsonString;
|
|---|
| 2959 | +import javax.json.JsonValue;
|
|---|
| 2960 | +
|
|---|
| 2961 | +/**
|
|---|
| 2962 | + * A MapBox vector style expression (immutable)
|
|---|
| 2963 | + * @author Taylor Smock
|
|---|
| 2964 | + * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/">https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/</a>
|
|---|
| 2965 | + * @since xxx
|
|---|
| 2966 | + */
|
|---|
| 2967 | +public final class Expression {
|
|---|
| 2968 | + /** An empty expression to use */
|
|---|
| 2969 | + public static final Expression EMPTY_EXPRESSION = new Expression(JsonValue.NULL);
|
|---|
| 2970 | + private static final String EMPTY_STRING = "";
|
|---|
| 2971 | +
|
|---|
| 2972 | + private final String mapcssFilterExpression;
|
|---|
| 2973 | +
|
|---|
| 2974 | + /**
|
|---|
| 2975 | + * Create a new filter expression. <i>Please note that this currently only supports basic comparators!</i>
|
|---|
| 2976 | + * @param value The value to parse
|
|---|
| 2977 | + */
|
|---|
| 2978 | + public Expression(JsonValue value) {
|
|---|
| 2979 | + if (value.getValueType() == JsonValue.ValueType.ARRAY) {
|
|---|
| 2980 | + final JsonArray array = value.asJsonArray();
|
|---|
| 2981 | + if (!array.isEmpty() && array.get(0).getValueType() == JsonValue.ValueType.STRING) {
|
|---|
| 2982 | + if ("==".equals(array.getString(0))) {
|
|---|
| 2983 | + // The mapcss equivalent of == is = (for the most part)
|
|---|
| 2984 | + this.mapcssFilterExpression = convertToString(array.get(1)) + "=" + convertToString(array.get(2));
|
|---|
| 2985 | + } else if (Arrays.asList("<=", ">=", ">", "<", "!=").contains(array.getString(0))) {
|
|---|
| 2986 | + this.mapcssFilterExpression = convertToString(array.get(1)) + array.getString(0) + convertToString(array.get(2));
|
|---|
| 2987 | + } else {
|
|---|
| 2988 | + this.mapcssFilterExpression = EMPTY_STRING;
|
|---|
| 2989 | + }
|
|---|
| 2990 | + } else {
|
|---|
| 2991 | + this.mapcssFilterExpression = EMPTY_STRING;
|
|---|
| 2992 | + }
|
|---|
| 2993 | + } else {
|
|---|
| 2994 | + this.mapcssFilterExpression = EMPTY_STRING;
|
|---|
| 2995 | + }
|
|---|
| 2996 | + }
|
|---|
| 2997 | +
|
|---|
| 2998 | + /**
|
|---|
| 2999 | + * Convert a value to a string
|
|---|
| 3000 | + * @param value The value to convert
|
|---|
| 3001 | + * @return A string
|
|---|
| 3002 | + */
|
|---|
| 3003 | + private static String convertToString(JsonValue value) {
|
|---|
| 3004 | + switch (value.getValueType()) {
|
|---|
| 3005 | + case STRING:
|
|---|
| 3006 | + return ((JsonString) value).getString();
|
|---|
| 3007 | + case FALSE:
|
|---|
| 3008 | + return Boolean.FALSE.toString();
|
|---|
| 3009 | + case TRUE:
|
|---|
| 3010 | + return Boolean.TRUE.toString();
|
|---|
| 3011 | + case NUMBER:
|
|---|
| 3012 | + return value.toString();
|
|---|
| 3013 | + case ARRAY:
|
|---|
| 3014 | + return '['
|
|---|
| 3015 | + + ((JsonArray) value).stream().map(Expression::convertToString).collect(Collectors.joining(","))
|
|---|
| 3016 | + + ']';
|
|---|
| 3017 | + case OBJECT:
|
|---|
| 3018 | + return '{'
|
|---|
| 3019 | + + ((JsonObject) value).entrySet().stream()
|
|---|
| 3020 | + .map(entry -> entry.getKey() + ":" + convertToString(entry.getValue())).collect(
|
|---|
| 3021 | + Collectors.joining(","))
|
|---|
| 3022 | + + '}';
|
|---|
| 3023 | + case NULL:
|
|---|
| 3024 | + default:
|
|---|
| 3025 | + return EMPTY_STRING;
|
|---|
| 3026 | + }
|
|---|
| 3027 | + }
|
|---|
| 3028 | +
|
|---|
| 3029 | + @Override
|
|---|
| 3030 | + public String toString() {
|
|---|
| 3031 | + return !EMPTY_STRING.equals(this.mapcssFilterExpression) ? '[' + this.mapcssFilterExpression + ']' : EMPTY_STRING;
|
|---|
| 3032 | + }
|
|---|
| 3033 | +
|
|---|
| 3034 | + @Override
|
|---|
| 3035 | + public boolean equals(Object other) {
|
|---|
| 3036 | + if (other instanceof Expression) {
|
|---|
| 3037 | + Expression o = (Expression) other;
|
|---|
| 3038 | + return Objects.equals(this.mapcssFilterExpression, o.mapcssFilterExpression);
|
|---|
| 3039 | + }
|
|---|
| 3040 | + return false;
|
|---|
| 3041 | + }
|
|---|
| 3042 | +
|
|---|
| 3043 | + @Override
|
|---|
| 3044 | + public int hashCode() {
|
|---|
| 3045 | + return Objects.hash(this.mapcssFilterExpression);
|
|---|
| 3046 | + }
|
|---|
| 3047 | +}
|
|---|
| 3048 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 3049 | new file mode 100644
|
|---|
| 3050 | index 000000000..9488c3d19
|
|---|
| 3051 | --- /dev/null
|
|---|
| 3052 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 3053 | @@ -0,0 +1,519 @@
|
|---|
| 3054 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 3055 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 3056 | +
|
|---|
| 3057 | +import java.awt.Font;
|
|---|
| 3058 | +import java.awt.GraphicsEnvironment;
|
|---|
| 3059 | +import java.text.MessageFormat;
|
|---|
| 3060 | +import java.util.Arrays;
|
|---|
| 3061 | +import java.util.Collection;
|
|---|
| 3062 | +import java.util.List;
|
|---|
| 3063 | +import java.util.Locale;
|
|---|
| 3064 | +import java.util.Objects;
|
|---|
| 3065 | +import java.util.regex.Matcher;
|
|---|
| 3066 | +import java.util.regex.Pattern;
|
|---|
| 3067 | +import java.util.stream.Collectors;
|
|---|
| 3068 | +import java.util.stream.Stream;
|
|---|
| 3069 | +
|
|---|
| 3070 | +import javax.json.JsonArray;
|
|---|
| 3071 | +import javax.json.JsonNumber;
|
|---|
| 3072 | +import javax.json.JsonObject;
|
|---|
| 3073 | +import javax.json.JsonString;
|
|---|
| 3074 | +import javax.json.JsonValue;
|
|---|
| 3075 | +
|
|---|
| 3076 | +/**
|
|---|
| 3077 | + * MapBox style layers
|
|---|
| 3078 | + * @author Taylor Smock
|
|---|
| 3079 | + * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/">https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/</a>
|
|---|
| 3080 | + * @since xxx
|
|---|
| 3081 | + */
|
|---|
| 3082 | +public class Layers {
|
|---|
| 3083 | + /**
|
|---|
| 3084 | + * The layer type. This affects the rendering.
|
|---|
| 3085 | + * @author Taylor Smock
|
|---|
| 3086 | + * @since xxx
|
|---|
| 3087 | + */
|
|---|
| 3088 | + enum Type {
|
|---|
| 3089 | + /** Filled polygon with an (optional) border */
|
|---|
| 3090 | + FILL,
|
|---|
| 3091 | + /** A line */
|
|---|
| 3092 | + LINE,
|
|---|
| 3093 | + /** A symbol */
|
|---|
| 3094 | + SYMBOL,
|
|---|
| 3095 | + /** A circle */
|
|---|
| 3096 | + CIRCLE,
|
|---|
| 3097 | + /** A heatmap */
|
|---|
| 3098 | + HEATMAP,
|
|---|
| 3099 | + /** A 3D polygon extrusion */
|
|---|
| 3100 | + FILL_EXTRUSION,
|
|---|
| 3101 | + /** Raster */
|
|---|
| 3102 | + RASTER,
|
|---|
| 3103 | + /** Hillshade data */
|
|---|
| 3104 | + HILLSHADE,
|
|---|
| 3105 | + /** A background color or pattern */
|
|---|
| 3106 | + BACKGROUND,
|
|---|
| 3107 | + /** The fallback layer */
|
|---|
| 3108 | + SKY
|
|---|
| 3109 | + }
|
|---|
| 3110 | +
|
|---|
| 3111 | + private static final String EMPTY_STRING = "";
|
|---|
| 3112 | + private static final char SEMI_COLON = ';';
|
|---|
| 3113 | + private static final Pattern CURLY_BRACES = Pattern.compile("(\\{(.*?)})");
|
|---|
| 3114 | +
|
|---|
| 3115 | + /** A required unique layer name */
|
|---|
| 3116 | + private final String id;
|
|---|
| 3117 | + /** The required type */
|
|---|
| 3118 | + private final Type type;
|
|---|
| 3119 | + /** An optional expression */
|
|---|
| 3120 | + private final Expression filter;
|
|---|
| 3121 | + /** The max zoom for the layer */
|
|---|
| 3122 | + private final int maxZoom;
|
|---|
| 3123 | + /** The min zoom for the layer */
|
|---|
| 3124 | + private final int minZoom;
|
|---|
| 3125 | +
|
|---|
| 3126 | + /** Default paint properties for this layer */
|
|---|
| 3127 | + private final String paint;
|
|---|
| 3128 | +
|
|---|
| 3129 | + /** A source description to be used with this layer. Required for everything <i>but</i> {@link Type#BACKGROUND} */
|
|---|
| 3130 | + private final String source;
|
|---|
| 3131 | + /** Layer to use from the vector tile source. Only allowed with {@link SourceType#VECTOR}. */
|
|---|
| 3132 | + private final String sourceLayer;
|
|---|
| 3133 | + /** The id for the style -- used for image paths */
|
|---|
| 3134 | + private final String styleId;
|
|---|
| 3135 | + /**
|
|---|
| 3136 | + * Create a layer object
|
|---|
| 3137 | + * @param layerInfo The info to use to create the layer
|
|---|
| 3138 | + */
|
|---|
| 3139 | + public Layers(final JsonObject layerInfo) {
|
|---|
| 3140 | + this (null, layerInfo);
|
|---|
| 3141 | + }
|
|---|
| 3142 | +
|
|---|
| 3143 | + /**
|
|---|
| 3144 | + * Create a layer object
|
|---|
| 3145 | + * @param styleId The id for the style (image paths require this)
|
|---|
| 3146 | + * @param layerInfo The info to use to create the layer
|
|---|
| 3147 | + */
|
|---|
| 3148 | + public Layers(final String styleId, final JsonObject layerInfo) {
|
|---|
| 3149 | + this.id = layerInfo.getString("id");
|
|---|
| 3150 | + this.styleId = styleId;
|
|---|
| 3151 | + this.type = Type.valueOf(layerInfo.getString("type").replace("-", "_").toUpperCase(Locale.ROOT));
|
|---|
| 3152 | + if (layerInfo.containsKey("filter")) {
|
|---|
| 3153 | + this.filter = new Expression(layerInfo.get("filter"));
|
|---|
| 3154 | + } else {
|
|---|
| 3155 | + this.filter = Expression.EMPTY_EXPRESSION;
|
|---|
| 3156 | + }
|
|---|
| 3157 | + this.maxZoom = layerInfo.getInt("maxzoom", Integer.MAX_VALUE);
|
|---|
| 3158 | + this.minZoom = layerInfo.getInt("minzoom", Integer.MIN_VALUE);
|
|---|
| 3159 | + // There is a metadata field (I don't *think* I need it?)
|
|---|
| 3160 | + // source is only optional with {@link Type#BACKGROUND}.
|
|---|
| 3161 | + if (this.type == Type.BACKGROUND) {
|
|---|
| 3162 | + this.source = layerInfo.getString("source", null);
|
|---|
| 3163 | + } else {
|
|---|
| 3164 | + this.source = layerInfo.getString("source");
|
|---|
| 3165 | + }
|
|---|
| 3166 | + if (layerInfo.containsKey("paint") && layerInfo.get("paint").getValueType() == JsonValue.ValueType.OBJECT) {
|
|---|
| 3167 | + final JsonObject paintObject = layerInfo.getJsonObject("paint");
|
|---|
| 3168 | + final JsonObject layoutObject = layerInfo.getOrDefault("layout", JsonValue.EMPTY_JSON_OBJECT).asJsonObject();
|
|---|
| 3169 | + // Don't throw exceptions here, since we may just point at the styling
|
|---|
| 3170 | + if ("visible".equalsIgnoreCase(layoutObject.getString("visibility", "visible"))) {
|
|---|
| 3171 | + switch (type) {
|
|---|
| 3172 | + case FILL:
|
|---|
| 3173 | + // area
|
|---|
| 3174 | + this.paint = parsePaintFill(paintObject);
|
|---|
| 3175 | + break;
|
|---|
| 3176 | + case LINE:
|
|---|
| 3177 | + // way
|
|---|
| 3178 | + this.paint = parsePaintLine(layoutObject, paintObject);
|
|---|
| 3179 | + break;
|
|---|
| 3180 | + case CIRCLE:
|
|---|
| 3181 | + // point
|
|---|
| 3182 | + this.paint = parsePaintCircle(paintObject);
|
|---|
| 3183 | + break;
|
|---|
| 3184 | + case SYMBOL:
|
|---|
| 3185 | + // point
|
|---|
| 3186 | + this.paint = parsePaintSymbol(layoutObject, paintObject);
|
|---|
| 3187 | + break;
|
|---|
| 3188 | + case BACKGROUND:
|
|---|
| 3189 | + // canvas only
|
|---|
| 3190 | + this.paint = parsePaintBackground(paintObject);
|
|---|
| 3191 | + break;
|
|---|
| 3192 | + default:
|
|---|
| 3193 | + this.paint = EMPTY_STRING;
|
|---|
| 3194 | + }
|
|---|
| 3195 | + } else {
|
|---|
| 3196 | + this.paint = EMPTY_STRING;
|
|---|
| 3197 | + }
|
|---|
| 3198 | + } else {
|
|---|
| 3199 | + this.paint = EMPTY_STRING;
|
|---|
| 3200 | + }
|
|---|
| 3201 | + this.sourceLayer = layerInfo.getString("source-layer", null);
|
|---|
| 3202 | + }
|
|---|
| 3203 | +
|
|---|
| 3204 | + /**
|
|---|
| 3205 | + * Get the filter for this layer
|
|---|
| 3206 | + * @return The filter
|
|---|
| 3207 | + */
|
|---|
| 3208 | + public Expression getFilter() {
|
|---|
| 3209 | + return this.filter;
|
|---|
| 3210 | + }
|
|---|
| 3211 | +
|
|---|
| 3212 | + /**
|
|---|
| 3213 | + * Get the unique id for this layer
|
|---|
| 3214 | + * @return The unique id
|
|---|
| 3215 | + */
|
|---|
| 3216 | + public String getId() {
|
|---|
| 3217 | + return this.id;
|
|---|
| 3218 | + }
|
|---|
| 3219 | +
|
|---|
| 3220 | + /**
|
|---|
| 3221 | + * Get the type of this layer
|
|---|
| 3222 | + * @return The layer type
|
|---|
| 3223 | + */
|
|---|
| 3224 | + public Type getType() {
|
|---|
| 3225 | + return this.type;
|
|---|
| 3226 | + }
|
|---|
| 3227 | +
|
|---|
| 3228 | + private static String parsePaintLine(final JsonObject layoutObject, final JsonObject paintObject) {
|
|---|
| 3229 | + final StringBuilder sb = new StringBuilder(36);
|
|---|
| 3230 | + // line-blur, default 0 (px)
|
|---|
| 3231 | + // line-color, default #000000, disabled by line-pattern
|
|---|
| 3232 | + final String color = paintObject.getString("line-color", "#000000");
|
|---|
| 3233 | + sb.append("color:").append(color).append(SEMI_COLON);
|
|---|
| 3234 | + // line-opacity, default 1 (0-1)
|
|---|
| 3235 | + final JsonNumber opacity = paintObject.getJsonNumber("line-opacity");
|
|---|
| 3236 | + if (opacity != null) {
|
|---|
| 3237 | + sb.append("opacity:").append(opacity.numberValue().doubleValue()).append(SEMI_COLON);
|
|---|
| 3238 | + }
|
|---|
| 3239 | + // line-cap, default butt (butt|round|square)
|
|---|
| 3240 | + final String cap = layoutObject.getString("line-cap", "butt");
|
|---|
| 3241 | + sb.append("linecap:");
|
|---|
| 3242 | + switch (cap) {
|
|---|
| 3243 | + case "round":
|
|---|
| 3244 | + case "square":
|
|---|
| 3245 | + sb.append(cap);
|
|---|
| 3246 | + break;
|
|---|
| 3247 | + case "butt":
|
|---|
| 3248 | + default:
|
|---|
| 3249 | + sb.append("none");
|
|---|
| 3250 | + }
|
|---|
| 3251 | +
|
|---|
| 3252 | + sb.append(SEMI_COLON);
|
|---|
| 3253 | + // line-dasharray, array of number >= 0, units in line widths, disabled by line-pattern
|
|---|
| 3254 | + if (paintObject.containsKey("line-dasharray")) {
|
|---|
| 3255 | + final JsonArray dashArray = paintObject.getJsonArray("line-dasharray");
|
|---|
| 3256 | + sb.append("dashes:");
|
|---|
| 3257 | + sb.append(dashArray.stream().filter(JsonNumber.class::isInstance).map(JsonNumber.class::cast)
|
|---|
| 3258 | + .map(JsonNumber::toString).collect(Collectors.joining(",")));
|
|---|
| 3259 | + sb.append(SEMI_COLON);
|
|---|
| 3260 | + }
|
|---|
| 3261 | + // line-gap-width
|
|---|
| 3262 | + // line-gradient
|
|---|
| 3263 | + // line-join
|
|---|
| 3264 | + // line-miter-limit
|
|---|
| 3265 | + // line-offset
|
|---|
| 3266 | + // line-pattern TODO this first, since it disables stuff
|
|---|
| 3267 | + // line-round-limit
|
|---|
| 3268 | + // line-sort-key
|
|---|
| 3269 | + // line-translate
|
|---|
| 3270 | + // line-translate-anchor
|
|---|
| 3271 | + // line-width
|
|---|
| 3272 | + final JsonNumber width = paintObject.getJsonNumber("line-width");
|
|---|
| 3273 | + sb.append("width:").append(width == null ? 1 : width.toString()).append(SEMI_COLON);
|
|---|
| 3274 | + return sb.toString();
|
|---|
| 3275 | + }
|
|---|
| 3276 | +
|
|---|
| 3277 | + private static String parsePaintCircle(final JsonObject paintObject) {
|
|---|
| 3278 | + final StringBuilder sb = new StringBuilder(150).append("symbol-shape:circle;")
|
|---|
| 3279 | + // circle-blur
|
|---|
| 3280 | + // circle-color
|
|---|
| 3281 | + .append("symbol-fill-color:").append(paintObject.getString("circle-color", "#000000")).append(SEMI_COLON);
|
|---|
| 3282 | + // circle-opacity
|
|---|
| 3283 | + final JsonNumber fillOpacity = paintObject.getJsonNumber("circle-opacity");
|
|---|
| 3284 | + sb.append("symbol-fill-opacity:").append(fillOpacity != null ? fillOpacity.numberValue().toString() : "1").append(SEMI_COLON);
|
|---|
| 3285 | + // circle-pitch-alignment // not 3D
|
|---|
| 3286 | + // circle-pitch-scale // not 3D
|
|---|
| 3287 | + // circle-radius
|
|---|
| 3288 | + final JsonNumber radius = paintObject.getJsonNumber("circle-radius");
|
|---|
| 3289 | + sb.append("symbol-size:").append(radius != null ? (2 * radius.numberValue().doubleValue()) : "10").append(SEMI_COLON)
|
|---|
| 3290 | + // circle-sort-key
|
|---|
| 3291 | + // circle-stroke-color
|
|---|
| 3292 | + .append("symbol-stroke-color:").append(paintObject.getString("circle-stroke-color", "#000000")).append(SEMI_COLON);
|
|---|
| 3293 | + // circle-stroke-opacity
|
|---|
| 3294 | + final JsonNumber strokeOpacity = paintObject.getJsonNumber("circle-stroke-opacity");
|
|---|
| 3295 | + sb.append("symbol-stroke-opacity:").append(strokeOpacity != null ? strokeOpacity.numberValue().toString() : "1").append(SEMI_COLON);
|
|---|
| 3296 | + // circle-stroke-width
|
|---|
| 3297 | + final JsonNumber strokeWidth = paintObject.getJsonNumber("circle-stroke-width");
|
|---|
| 3298 | + sb.append("symbol-stroke-width:").append(strokeWidth != null ? strokeWidth.numberValue().toString() : "0").append(SEMI_COLON);
|
|---|
| 3299 | + // circle-translate
|
|---|
| 3300 | + // circle-translate-anchor
|
|---|
| 3301 | + return sb.toString();
|
|---|
| 3302 | + }
|
|---|
| 3303 | +
|
|---|
| 3304 | + private String parsePaintSymbol(
|
|---|
| 3305 | + final JsonObject layoutObject,
|
|---|
| 3306 | + final JsonObject paintObject) {
|
|---|
| 3307 | + final StringBuilder sb = new StringBuilder();
|
|---|
| 3308 | + // icon-allow-overlap
|
|---|
| 3309 | + // icon-anchor
|
|---|
| 3310 | + // icon-color
|
|---|
| 3311 | + // icon-halo-blur
|
|---|
| 3312 | + // icon-halo-color
|
|---|
| 3313 | + // icon-halo-width
|
|---|
| 3314 | + // icon-ignore-placement
|
|---|
| 3315 | + // icon-image
|
|---|
| 3316 | + boolean iconImage = false;
|
|---|
| 3317 | + if (layoutObject.containsKey("icon-image")) {
|
|---|
| 3318 | + sb.append("icon-image:concat(");
|
|---|
| 3319 | + if (this.styleId != null && !this.styleId.trim().isEmpty()) {
|
|---|
| 3320 | + sb.append('"').append(this.styleId).append('/').append("\",");
|
|---|
| 3321 | + }
|
|---|
| 3322 | + Matcher matcher = CURLY_BRACES.matcher(layoutObject.getString("icon-image"));
|
|---|
| 3323 | + StringBuffer stringBuffer = new StringBuffer();
|
|---|
| 3324 | + int previousMatch;
|
|---|
| 3325 | + if (matcher.lookingAt()) {
|
|---|
| 3326 | + matcher.appendReplacement(stringBuffer, "tag(\"$2\"),\"");
|
|---|
| 3327 | + previousMatch = matcher.end();
|
|---|
| 3328 | + } else {
|
|---|
| 3329 | + previousMatch = 0;
|
|---|
| 3330 | + stringBuffer.append('"');
|
|---|
| 3331 | + }
|
|---|
| 3332 | + while (matcher.find()) {
|
|---|
| 3333 | + if (matcher.start() == previousMatch) {
|
|---|
| 3334 | + matcher.appendReplacement(stringBuffer, ",tag(\"$2\")");
|
|---|
| 3335 | + } else {
|
|---|
| 3336 | + matcher.appendReplacement(stringBuffer, "\",tag(\"$2\"),\"");
|
|---|
| 3337 | + }
|
|---|
| 3338 | + previousMatch = matcher.end();
|
|---|
| 3339 | + }
|
|---|
| 3340 | + if (matcher.hitEnd() && stringBuffer.toString().endsWith(",\"")) {
|
|---|
| 3341 | + stringBuffer.delete(stringBuffer.length() - ",\"".length(), stringBuffer.length());
|
|---|
| 3342 | + } else if (!matcher.hitEnd()) {
|
|---|
| 3343 | + stringBuffer.append('"');
|
|---|
| 3344 | + }
|
|---|
| 3345 | + StringBuffer tail = new StringBuffer();
|
|---|
| 3346 | + matcher.appendTail(tail);
|
|---|
| 3347 | + if (tail.length() > 0) {
|
|---|
| 3348 | + String current = stringBuffer.toString();
|
|---|
| 3349 | + if (!"\"".equals(current) && !current.endsWith(",\"")) {
|
|---|
| 3350 | + stringBuffer.append(",\"");
|
|---|
| 3351 | + }
|
|---|
| 3352 | + stringBuffer.append(tail);
|
|---|
| 3353 | + stringBuffer.append('"');
|
|---|
| 3354 | + }
|
|---|
| 3355 | +
|
|---|
| 3356 | + sb.append(stringBuffer).append(')').append(SEMI_COLON);
|
|---|
| 3357 | + iconImage = true;
|
|---|
| 3358 | + }
|
|---|
| 3359 | + // icon-keep-upright
|
|---|
| 3360 | + // icon-offset
|
|---|
| 3361 | + if (iconImage && layoutObject.containsKey("icon-offset")) {
|
|---|
| 3362 | + // default [0, 0], right,down == positive, left,up == negative
|
|---|
| 3363 | + final List<JsonNumber> offset = layoutObject.getJsonArray("icon-offset").getValuesAs(JsonNumber.class);
|
|---|
| 3364 | + // Assume that the offset must be size 2. Probably not necessary, but docs aren't necessary clear.
|
|---|
| 3365 | + if (offset.size() == 2) {
|
|---|
| 3366 | + sb.append("icon-offset-x:").append(offset.get(0).doubleValue()).append(SEMI_COLON)
|
|---|
| 3367 | + .append("icon-offset-y:").append(offset.get(1).doubleValue()).append(SEMI_COLON);
|
|---|
| 3368 | + }
|
|---|
| 3369 | + }
|
|---|
| 3370 | + // icon-opacity
|
|---|
| 3371 | + if (iconImage && paintObject.containsKey("icon-opacity")) {
|
|---|
| 3372 | + final double opacity = paintObject.getJsonNumber("icon-opacity").doubleValue();
|
|---|
| 3373 | + sb.append("icon-opacity:").append(opacity).append(SEMI_COLON);
|
|---|
| 3374 | + }
|
|---|
| 3375 | + // icon-optional
|
|---|
| 3376 | + // icon-padding
|
|---|
| 3377 | + // icon-pitch-alignment
|
|---|
| 3378 | + // icon-rotate
|
|---|
| 3379 | + if (iconImage && layoutObject.containsKey("icon-rotate")) {
|
|---|
| 3380 | + final double rotation = layoutObject.getJsonNumber("icon-rotate").doubleValue();
|
|---|
| 3381 | + sb.append("icon-rotation:").append(rotation).append(SEMI_COLON);
|
|---|
| 3382 | + }
|
|---|
| 3383 | + // icon-rotation-alignment
|
|---|
| 3384 | + // icon-size
|
|---|
| 3385 | + // icon-text-fit
|
|---|
| 3386 | + // icon-text-fit-padding
|
|---|
| 3387 | + // icon-translate
|
|---|
| 3388 | + // icon-translate-anchor
|
|---|
| 3389 | + // symbol-avoid-edges
|
|---|
| 3390 | + // symbol-placement
|
|---|
| 3391 | + // symbol-sort-key
|
|---|
| 3392 | + // symbol-spacing
|
|---|
| 3393 | + // symbol-z-order
|
|---|
| 3394 | + // text-allow-overlap
|
|---|
| 3395 | + // text-anchor
|
|---|
| 3396 | + // text-color
|
|---|
| 3397 | + if (paintObject.containsKey("text-color")) {
|
|---|
| 3398 | + sb.append("text-color:").append(paintObject.getString("text-color")).append(SEMI_COLON);
|
|---|
| 3399 | + }
|
|---|
| 3400 | + // text-field
|
|---|
| 3401 | + if (layoutObject.containsKey("text-field")) {
|
|---|
| 3402 | + sb.append("text:")
|
|---|
| 3403 | + .append(layoutObject.getString("text-field").replace("}", EMPTY_STRING).replace("{", EMPTY_STRING))
|
|---|
| 3404 | + .append(SEMI_COLON);
|
|---|
| 3405 | + }
|
|---|
| 3406 | + // text-font
|
|---|
| 3407 | + if (layoutObject.containsKey("text-font")) {
|
|---|
| 3408 | + List<String> fonts = layoutObject.getJsonArray("text-font").stream().filter(JsonString.class::isInstance)
|
|---|
| 3409 | + .map(JsonString.class::cast).map(JsonString::getString).collect(Collectors.toList());
|
|---|
| 3410 | + Font[] systemFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
|---|
| 3411 | + for (String fontString : fonts) {
|
|---|
| 3412 | + Collection<Font> fontMatches = Stream.of(systemFonts)
|
|---|
| 3413 | + .filter(font -> Arrays.asList(font.getName(), font.getFontName(), font.getFamily(), font.getPSName()).contains(fontString))
|
|---|
| 3414 | + .collect(Collectors.toList());
|
|---|
| 3415 | + if (!fontMatches.isEmpty()) {
|
|---|
| 3416 | + final Font setFont = fontMatches.stream().filter(font -> font.getName().equals(fontString)).findAny()
|
|---|
| 3417 | + .orElseGet(() -> fontMatches.stream().filter(font -> font.getFontName().equals(fontString)).findAny()
|
|---|
| 3418 | + .orElseGet(() -> fontMatches.stream().filter(font -> font.getPSName().equals(fontString)).findAny()
|
|---|
| 3419 | + .orElseGet(() -> fontMatches.stream().filter(font -> font.getFamily().equals(fontString)).findAny().orElse(null))));
|
|---|
| 3420 | + if (setFont != null) {
|
|---|
| 3421 | + sb.append("font-family:\"").append(setFont.getFamily()).append('"').append(SEMI_COLON);
|
|---|
| 3422 | + sb.append("font-weight:").append(setFont.isBold() ? "bold" : "normal").append(SEMI_COLON);
|
|---|
| 3423 | + sb.append("font-style:").append(setFont.isItalic() ? "italic" : "normal").append(SEMI_COLON);
|
|---|
| 3424 | + break;
|
|---|
| 3425 | + }
|
|---|
| 3426 | + }
|
|---|
| 3427 | + }
|
|---|
| 3428 | + }
|
|---|
| 3429 | + // text-halo-blur
|
|---|
| 3430 | + // text-halo-color
|
|---|
| 3431 | + if (paintObject.containsKey("text-halo-color")) {
|
|---|
| 3432 | + sb.append("text-halo-color:").append(paintObject.getString("text-halo-color")).append(SEMI_COLON);
|
|---|
| 3433 | + }
|
|---|
| 3434 | + // text-halo-width
|
|---|
| 3435 | + if (paintObject.containsKey("text-halo-width")) {
|
|---|
| 3436 | + sb.append("text-halo-radius:").append(paintObject.getJsonNumber("text-halo-width").intValue()).append(SEMI_COLON);
|
|---|
| 3437 | + }
|
|---|
| 3438 | + // text-ignore-placement
|
|---|
| 3439 | + // text-justify
|
|---|
| 3440 | + // text-keep-upright
|
|---|
| 3441 | + // text-letter-spacing
|
|---|
| 3442 | + // text-line-height
|
|---|
| 3443 | + // text-max-angle
|
|---|
| 3444 | + // text-max-width
|
|---|
| 3445 | + // text-offset
|
|---|
| 3446 | + // text-opacity
|
|---|
| 3447 | + if (paintObject.containsKey("text-opacity")) {
|
|---|
| 3448 | + sb.append("text-opacity:").append(paintObject.getJsonNumber("text-opacity").doubleValue()).append(SEMI_COLON);
|
|---|
| 3449 | + }
|
|---|
| 3450 | + // text-optional
|
|---|
| 3451 | + // text-padding
|
|---|
| 3452 | + // text-pitch-alignment
|
|---|
| 3453 | + // text-radial-offset
|
|---|
| 3454 | + // text-rotate
|
|---|
| 3455 | + // text-rotation-alignment
|
|---|
| 3456 | + // text-size
|
|---|
| 3457 | + final JsonNumber textSize = layoutObject.getJsonNumber("text-size");
|
|---|
| 3458 | + sb.append("font-size:").append(textSize != null ? textSize.numberValue().toString() : "16").append(SEMI_COLON);
|
|---|
| 3459 | + // text-transform
|
|---|
| 3460 | + // text-translate
|
|---|
| 3461 | + // text-translate-anchor
|
|---|
| 3462 | + // text-variable-anchor
|
|---|
| 3463 | + // text-writing-mode
|
|---|
| 3464 | + return sb.toString();
|
|---|
| 3465 | + }
|
|---|
| 3466 | +
|
|---|
| 3467 | + private static String parsePaintBackground(final JsonObject paintObject) {
|
|---|
| 3468 | + final StringBuilder sb = new StringBuilder(20);
|
|---|
| 3469 | + // background-color
|
|---|
| 3470 | + final String bgColor = paintObject.getString("background-color", null);
|
|---|
| 3471 | + if (bgColor != null) {
|
|---|
| 3472 | + sb.append("fill-color:").append(bgColor).append(SEMI_COLON);
|
|---|
| 3473 | + }
|
|---|
| 3474 | + // background-opacity
|
|---|
| 3475 | + // background-pattern
|
|---|
| 3476 | + return sb.toString();
|
|---|
| 3477 | + }
|
|---|
| 3478 | +
|
|---|
| 3479 | + private static String parsePaintFill(final JsonObject paintObject) {
|
|---|
| 3480 | + StringBuilder sb = new StringBuilder(50)
|
|---|
| 3481 | + // fill-antialias
|
|---|
| 3482 | + // fill-color
|
|---|
| 3483 | + .append("fill-color:").append(paintObject.getString("fill-color", "#000000")).append(SEMI_COLON);
|
|---|
| 3484 | + // fill-opacity
|
|---|
| 3485 | + final JsonNumber opacity = paintObject.getJsonNumber("fill-opacity");
|
|---|
| 3486 | + sb.append("fill-opacity:").append(opacity != null ? opacity.numberValue().toString() : "1").append(SEMI_COLON)
|
|---|
| 3487 | + // fill-outline-color
|
|---|
| 3488 | + .append("color:").append(paintObject.getString("fill-outline-color",
|
|---|
| 3489 | + paintObject.getString("fill-color", "#000000"))).append(SEMI_COLON);
|
|---|
| 3490 | + // fill-pattern
|
|---|
| 3491 | + // fill-sort-key
|
|---|
| 3492 | + // fill-translate
|
|---|
| 3493 | + // fill-translate-anchor
|
|---|
| 3494 | + return sb.toString();
|
|---|
| 3495 | + }
|
|---|
| 3496 | +
|
|---|
| 3497 | + /**
|
|---|
| 3498 | + * Converts this layer object to a mapcss entry string (to be parsed later)
|
|---|
| 3499 | + * @return The mapcss entry (string form)
|
|---|
| 3500 | + */
|
|---|
| 3501 | + @Override
|
|---|
| 3502 | + public String toString() {
|
|---|
| 3503 | + if (this.filter.toString().isEmpty() && this.paint.isEmpty()) {
|
|---|
| 3504 | + return EMPTY_STRING;
|
|---|
| 3505 | + } else if (this.type == Type.BACKGROUND) {
|
|---|
| 3506 | + // AFAIK, paint has no zoom levels, and doesn't accept a layer
|
|---|
| 3507 | + return "canvas{" + this.paint + "}";
|
|---|
| 3508 | + }
|
|---|
| 3509 | +
|
|---|
| 3510 | + final String zoomSelector;
|
|---|
| 3511 | + if (this.minZoom == this.maxZoom) {
|
|---|
| 3512 | + zoomSelector = "|z" + this.minZoom;
|
|---|
| 3513 | + } else if (this.minZoom > Integer.MIN_VALUE && this.maxZoom == Integer.MAX_VALUE) {
|
|---|
| 3514 | + zoomSelector = "|z" + this.minZoom + "-";
|
|---|
| 3515 | + } else if (this.minZoom == Integer.MIN_VALUE && this.maxZoom < Integer.MAX_VALUE) {
|
|---|
| 3516 | + zoomSelector = "|z-" + this.maxZoom;
|
|---|
| 3517 | + } else if (this.minZoom > Integer.MIN_VALUE) {
|
|---|
| 3518 | + zoomSelector = MessageFormat.format("|z{0}-{1}", this.minZoom, this.maxZoom);
|
|---|
| 3519 | + } else {
|
|---|
| 3520 | + zoomSelector = EMPTY_STRING;
|
|---|
| 3521 | + }
|
|---|
| 3522 | + final String commonData = zoomSelector + this.filter.toString() + "::" + this.id + "{" + this.paint + "}";
|
|---|
| 3523 | +
|
|---|
| 3524 | + if (this.type == Type.CIRCLE || this.type == Type.SYMBOL) {
|
|---|
| 3525 | + return "node" + commonData;
|
|---|
| 3526 | + } else if (this.type == Type.FILL) {
|
|---|
| 3527 | + return "area" + commonData;
|
|---|
| 3528 | + } else if (this.type == Type.LINE) {
|
|---|
| 3529 | + return "way" + commonData;
|
|---|
| 3530 | + }
|
|---|
| 3531 | + return super.toString();
|
|---|
| 3532 | + }
|
|---|
| 3533 | +
|
|---|
| 3534 | + /**
|
|---|
| 3535 | + * Get the source that this applies to
|
|---|
| 3536 | + * @return The source name
|
|---|
| 3537 | + */
|
|---|
| 3538 | + public String getSource() {
|
|---|
| 3539 | + return this.source;
|
|---|
| 3540 | + }
|
|---|
| 3541 | +
|
|---|
| 3542 | + /**
|
|---|
| 3543 | + * Get the layer that this applies to
|
|---|
| 3544 | + * @return The layer name
|
|---|
| 3545 | + */
|
|---|
| 3546 | + public String getSourceLayer() {
|
|---|
| 3547 | + return this.sourceLayer;
|
|---|
| 3548 | + }
|
|---|
| 3549 | +
|
|---|
| 3550 | + @Override
|
|---|
| 3551 | + public boolean equals(Object other) {
|
|---|
| 3552 | + if (other != null && this.getClass() == other.getClass()) {
|
|---|
| 3553 | + Layers o = (Layers) other;
|
|---|
| 3554 | + return this.type == o.type
|
|---|
| 3555 | + && this.minZoom == o.minZoom
|
|---|
| 3556 | + && this.maxZoom == o.maxZoom
|
|---|
| 3557 | + && Objects.equals(this.id, o.id)
|
|---|
| 3558 | + && Objects.equals(this.styleId, o.styleId)
|
|---|
| 3559 | + && Objects.equals(this.sourceLayer, o.sourceLayer)
|
|---|
| 3560 | + && Objects.equals(this.source, o.source)
|
|---|
| 3561 | + && Objects.equals(this.filter, o.filter)
|
|---|
| 3562 | + && Objects.equals(this.paint, o.paint);
|
|---|
| 3563 | + }
|
|---|
| 3564 | + return false;
|
|---|
| 3565 | + }
|
|---|
| 3566 | +
|
|---|
| 3567 | + @Override
|
|---|
| 3568 | + public int hashCode() {
|
|---|
| 3569 | + return Objects.hash(this.type, this.minZoom, this.maxZoom, this.id, this.styleId, this.sourceLayer, this.source,
|
|---|
| 3570 | + this.filter, this.paint);
|
|---|
| 3571 | + }
|
|---|
| 3572 | +}
|
|---|
| 3573 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 3574 | new file mode 100644
|
|---|
| 3575 | index 000000000..746913042
|
|---|
| 3576 | --- /dev/null
|
|---|
| 3577 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 3578 | @@ -0,0 +1,266 @@
|
|---|
| 3579 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 3580 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 3581 | +
|
|---|
| 3582 | +import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 3583 | +
|
|---|
| 3584 | +import java.awt.Image;
|
|---|
| 3585 | +import java.awt.image.BufferedImage;
|
|---|
| 3586 | +import java.io.BufferedReader;
|
|---|
| 3587 | +import java.io.File;
|
|---|
| 3588 | +import java.io.IOException;
|
|---|
| 3589 | +import java.io.InputStream;
|
|---|
| 3590 | +import java.io.OutputStream;
|
|---|
| 3591 | +import java.nio.charset.StandardCharsets;
|
|---|
| 3592 | +import java.nio.file.Files;
|
|---|
| 3593 | +import java.util.Collections;
|
|---|
| 3594 | +import java.util.LinkedHashMap;
|
|---|
| 3595 | +import java.util.List;
|
|---|
| 3596 | +import java.util.Map;
|
|---|
| 3597 | +import java.util.Objects;
|
|---|
| 3598 | +import java.util.Optional;
|
|---|
| 3599 | +import java.util.concurrent.ConcurrentHashMap;
|
|---|
| 3600 | +import java.util.stream.Collectors;
|
|---|
| 3601 | +
|
|---|
| 3602 | +import javax.imageio.ImageIO;
|
|---|
| 3603 | +import javax.json.Json;
|
|---|
| 3604 | +import javax.json.JsonArray;
|
|---|
| 3605 | +import javax.json.JsonObject;
|
|---|
| 3606 | +import javax.json.JsonReader;
|
|---|
| 3607 | +import javax.json.JsonStructure;
|
|---|
| 3608 | +import javax.json.JsonValue;
|
|---|
| 3609 | +
|
|---|
| 3610 | +import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
|
|---|
| 3611 | +import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 3612 | +import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 3613 | +import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
|
|---|
| 3614 | +import org.openstreetmap.josm.io.CachedFile;
|
|---|
| 3615 | +import org.openstreetmap.josm.spi.preferences.Config;
|
|---|
| 3616 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 3617 | +
|
|---|
| 3618 | +/**
|
|---|
| 3619 | + * Create a mapping for a Mapbox Vector Style
|
|---|
| 3620 | + *
|
|---|
| 3621 | + * @author Taylor Smock
|
|---|
| 3622 | + * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/">https://docs.mapbox.com/mapbox-gl-js/style-spec/</a>
|
|---|
| 3623 | + * @since xxx
|
|---|
| 3624 | + */
|
|---|
| 3625 | +public class MapBoxVectorStyle {
|
|---|
| 3626 | +
|
|---|
| 3627 | + private static final ConcurrentHashMap<String, MapBoxVectorStyle> STYLE_MAPPING = new ConcurrentHashMap<>();
|
|---|
| 3628 | +
|
|---|
| 3629 | + /**
|
|---|
| 3630 | + * Get a MapBoxVector style for a URL
|
|---|
| 3631 | + * @param url The url to get
|
|---|
| 3632 | + * @return The MapBox Vector Style. May be {@code null} if there was an error.
|
|---|
| 3633 | + */
|
|---|
| 3634 | + public static MapBoxVectorStyle getMapBoxVectorStyle(String url) {
|
|---|
| 3635 | + return STYLE_MAPPING.computeIfAbsent(url, key -> {
|
|---|
| 3636 | + try (CachedFile style = new CachedFile(url);
|
|---|
| 3637 | + BufferedReader reader = style.getContentReader();
|
|---|
| 3638 | + JsonReader jsonReader = Json.createReader(reader)) {
|
|---|
| 3639 | + JsonStructure structure = jsonReader.read();
|
|---|
| 3640 | + return new MapBoxVectorStyle(structure.asJsonObject());
|
|---|
| 3641 | + } catch (IOException e) {
|
|---|
| 3642 | + Logging.error(e);
|
|---|
| 3643 | + }
|
|---|
| 3644 | + // Documentation indicates that this will <i>not</i> be entered into the map, which means that this will be
|
|---|
| 3645 | + // retried if something goes wrong.
|
|---|
| 3646 | + return null;
|
|---|
| 3647 | + });
|
|---|
| 3648 | + }
|
|---|
| 3649 | +
|
|---|
| 3650 | + /** The version for the style specification */
|
|---|
| 3651 | + private final int version;
|
|---|
| 3652 | + /** The optional name for the vector style */
|
|---|
| 3653 | + private final String name;
|
|---|
| 3654 | + /** The optional URL for sprites. This mush be absolute (so it must contain the scheme, authority, and path). */
|
|---|
| 3655 | + private final String spriteUrl;
|
|---|
| 3656 | + /** The optional URL for glyphs. This may have replaceable values in it. */
|
|---|
| 3657 | + private final String glyphUrl;
|
|---|
| 3658 | + /** The required collection of sources with a list of layers that are applicable for that source*/
|
|---|
| 3659 | + private final Map<Source, ElemStyles> sources;
|
|---|
| 3660 | +
|
|---|
| 3661 | + /**
|
|---|
| 3662 | + * Create a new MapBoxVector style. You should prefer {@link #getMapBoxVectorStyle(String)}
|
|---|
| 3663 | + * for deduplication purposes.
|
|---|
| 3664 | + *
|
|---|
| 3665 | + * @param jsonObject The object to create the style from
|
|---|
| 3666 | + * @see #getMapBoxVectorStyle(String)
|
|---|
| 3667 | + */
|
|---|
| 3668 | + public MapBoxVectorStyle(JsonObject jsonObject) {
|
|---|
| 3669 | + // There should be a version specifier. We currently only support version 8.
|
|---|
| 3670 | + // This can throw an NPE when there is no version number.
|
|---|
| 3671 | + this.version = jsonObject.getInt("version");
|
|---|
| 3672 | + if (this.version == 8) {
|
|---|
| 3673 | + this.name = jsonObject.getString("name", null);
|
|---|
| 3674 | + String id = jsonObject.getString("id", this.name);
|
|---|
| 3675 | + this.spriteUrl = jsonObject.getString("sprite", null);
|
|---|
| 3676 | + this.glyphUrl = jsonObject.getString("glyphs", null);
|
|---|
| 3677 | + final List<Source> sourceList;
|
|---|
| 3678 | + if (jsonObject.containsKey("sources") && jsonObject.get("sources").getValueType() == JsonValue.ValueType.OBJECT) {
|
|---|
| 3679 | + final JsonObject sourceObj = jsonObject.getJsonObject("sources");
|
|---|
| 3680 | + sourceList = sourceObj.entrySet().stream().filter(entry -> entry.getValue().getValueType() == JsonValue.ValueType.OBJECT)
|
|---|
| 3681 | + .map(entry -> new Source(entry.getKey(), entry.getValue().asJsonObject())).collect(Collectors.toList());
|
|---|
| 3682 | + } else {
|
|---|
| 3683 | + sourceList = Collections.emptyList();
|
|---|
| 3684 | + }
|
|---|
| 3685 | + final List<Layers> layers;
|
|---|
| 3686 | + if (jsonObject.containsKey("layers") && jsonObject.get("layers").getValueType() == JsonValue.ValueType.ARRAY) {
|
|---|
| 3687 | + JsonArray lArray = jsonObject.getJsonArray("layers");
|
|---|
| 3688 | + layers = lArray.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast).map(obj -> new Layers(id, obj))
|
|---|
| 3689 | + .collect(Collectors.toList());
|
|---|
| 3690 | + } else {
|
|---|
| 3691 | + layers = Collections.emptyList();
|
|---|
| 3692 | + }
|
|---|
| 3693 | + final Map<Optional<Source>, List<Layers>> sourceLayer = layers.stream().collect(
|
|---|
| 3694 | + Collectors.groupingBy(layer -> sourceList.stream().filter(source -> source.getName().equals(layer.getSource()))
|
|---|
| 3695 | + .findFirst(), LinkedHashMap::new, Collectors.toList()));
|
|---|
| 3696 | + // Abuse HashMap null (null == default)
|
|---|
| 3697 | + this.sources = new LinkedHashMap<>();
|
|---|
| 3698 | + for (Map.Entry<Optional<Source>, List<Layers>> entry : sourceLayer.entrySet()) {
|
|---|
| 3699 | + final Source source = entry.getKey().orElse(null);
|
|---|
| 3700 | + final String data = entry.getValue().stream().map(Layers::toString).collect(Collectors.joining());
|
|---|
| 3701 | + final String metaData = "meta{title:" + (source == null ? "Generated Style" :
|
|---|
| 3702 | + source.getName()) + ";version:\"autogenerated\";description:\"auto generated style\";}";
|
|---|
| 3703 | +
|
|---|
| 3704 | + // This is the default canvas
|
|---|
| 3705 | + final String canvas = "canvas{default-points:false;default-lines:false;}";
|
|---|
| 3706 | + final MapCSSStyleSource style = new MapCSSStyleSource(metaData + canvas + data);
|
|---|
| 3707 | + // Save to directory
|
|---|
| 3708 | + MainApplication.worker.execute(() -> this.save((source == null ? data.hashCode() : source.getName()) + ".mapcss", style));
|
|---|
| 3709 | + this.sources.put(source, new ElemStyles(Collections.singleton(style)));
|
|---|
| 3710 | + }
|
|---|
| 3711 | + if (this.spriteUrl != null && !this.spriteUrl.trim().isEmpty()) {
|
|---|
| 3712 | + MainApplication.worker.execute(this::fetchSprites);
|
|---|
| 3713 | + }
|
|---|
| 3714 | + } else {
|
|---|
| 3715 | + throw new IllegalArgumentException(tr("Vector Tile Style Version not understood: version {0} (json: {1})",
|
|---|
| 3716 | + this.version, jsonObject));
|
|---|
| 3717 | + }
|
|---|
| 3718 | + }
|
|---|
| 3719 | +
|
|---|
| 3720 | + /**
|
|---|
| 3721 | + * Fetch sprites. Please note that this is (literally) only png. Unfortunately.
|
|---|
| 3722 | + * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/">https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/</a>
|
|---|
| 3723 | + */
|
|---|
| 3724 | + private void fetchSprites() {
|
|---|
| 3725 | + // HiDPI images first -- if this succeeds, don't bother with the lower resolution (JOSM has no method to switch)
|
|---|
| 3726 | + try (CachedFile spriteJson = new CachedFile(this.spriteUrl + "@2x.json");
|
|---|
| 3727 | + CachedFile spritePng = new CachedFile(this.spriteUrl + "@2x.png")) {
|
|---|
| 3728 | + if (parseSprites(spriteJson, spritePng)) {
|
|---|
| 3729 | + return;
|
|---|
| 3730 | + }
|
|---|
| 3731 | + }
|
|---|
| 3732 | + try (CachedFile spriteJson = new CachedFile(this.spriteUrl + ".json");
|
|---|
| 3733 | + CachedFile spritePng = new CachedFile(this.spriteUrl + ".png")) {
|
|---|
| 3734 | + parseSprites(spriteJson, spritePng);
|
|---|
| 3735 | + }
|
|---|
| 3736 | + }
|
|---|
| 3737 | +
|
|---|
| 3738 | + private boolean parseSprites(CachedFile spriteJson, CachedFile spritePng) {
|
|---|
| 3739 | + /* JSON looks like this:
|
|---|
| 3740 | + * { "image-name": {"width": width, "height": height, "x": x, "y": y, "pixelRatio": 1 }}
|
|---|
| 3741 | + * width/height are the dimensions of the image
|
|---|
| 3742 | + * x -- distance right from top left
|
|---|
| 3743 | + * y -- distance down from top left
|
|---|
| 3744 | + * pixelRatio -- this <i>appears</i> to be from the "@2x" (default 1)
|
|---|
| 3745 | + * content -- [left, top corner, right, bottom corner]
|
|---|
| 3746 | + * stretchX -- [[from, to], [from, to], ...]
|
|---|
| 3747 | + * stretchY -- [[from, to], [from, to], ...]
|
|---|
| 3748 | + */
|
|---|
| 3749 | + final JsonObject spriteObject;
|
|---|
| 3750 | + final BufferedImage spritePngImage;
|
|---|
| 3751 | + try (BufferedReader spriteJsonBufferedReader = spriteJson.getContentReader();
|
|---|
| 3752 | + JsonReader spriteJsonReader = Json.createReader(spriteJsonBufferedReader);
|
|---|
| 3753 | + InputStream spritePngBufferedReader = spritePng.getInputStream()
|
|---|
| 3754 | + ) {
|
|---|
| 3755 | + spriteObject = spriteJsonReader.read().asJsonObject();
|
|---|
| 3756 | + spritePngImage = ImageIO.read(spritePngBufferedReader);
|
|---|
| 3757 | + } catch (IOException e) {
|
|---|
| 3758 | + Logging.error(e);
|
|---|
| 3759 | + return false;
|
|---|
| 3760 | + }
|
|---|
| 3761 | + for (Map.Entry<String, JsonValue> entry : spriteObject.entrySet()) {
|
|---|
| 3762 | + final JsonObject info = entry.getValue().asJsonObject();
|
|---|
| 3763 | + int width = info.getInt("width");
|
|---|
| 3764 | + int height = info.getInt("height");
|
|---|
| 3765 | + int x = info.getInt("x");
|
|---|
| 3766 | + int y = info.getInt("y");
|
|---|
| 3767 | + save(entry.getKey() + ".png", spritePngImage.getSubimage(x, y, width, height));
|
|---|
| 3768 | + }
|
|---|
| 3769 | + return true;
|
|---|
| 3770 | + }
|
|---|
| 3771 | +
|
|---|
| 3772 | + private void save(String name, Object object) {
|
|---|
| 3773 | + final File cache;
|
|---|
| 3774 | + if (object instanceof Image) {
|
|---|
| 3775 | + // Images have a specific location where they are looked for
|
|---|
| 3776 | + cache = new File(Config.getDirs().getUserDataDirectory(true), "images");
|
|---|
| 3777 | + } else {
|
|---|
| 3778 | + cache = JosmBaseDirectories.getInstance().getCacheDirectory(true);
|
|---|
| 3779 | + }
|
|---|
| 3780 | + final File location = new File(cache, this.name != null ? this.name : Integer.toString(this.hashCode()));
|
|---|
| 3781 | + if ((!location.exists() && !location.mkdirs()) || (location.exists() && !location.isDirectory())) {
|
|---|
| 3782 | + // Don't try to save if the file exists and is not a directory or we couldn't create it
|
|---|
| 3783 | + return;
|
|---|
| 3784 | + }
|
|---|
| 3785 | + final File toSave = new File(location, name);
|
|---|
| 3786 | + try (OutputStream fileOutputStream = Files.newOutputStream(toSave.toPath())) {
|
|---|
| 3787 | + if (object instanceof String) {
|
|---|
| 3788 | + fileOutputStream.write(((String) object).getBytes(StandardCharsets.UTF_8));
|
|---|
| 3789 | + } else if (object instanceof MapCSSStyleSource) {
|
|---|
| 3790 | + MapCSSStyleSource source = (MapCSSStyleSource) object;
|
|---|
| 3791 | + try (InputStream inputStream = source.getSourceInputStream()) {
|
|---|
| 3792 | + int byteVal = inputStream.read();
|
|---|
| 3793 | + do {
|
|---|
| 3794 | + fileOutputStream.write(byteVal);
|
|---|
| 3795 | + byteVal = inputStream.read();
|
|---|
| 3796 | + } while (byteVal > -1);
|
|---|
| 3797 | + source.url = "file:/" + toSave.getAbsolutePath().replace('\\', '/');
|
|---|
| 3798 | + if (source.isLoaded()) {
|
|---|
| 3799 | + source.loadStyleSource();
|
|---|
| 3800 | + }
|
|---|
| 3801 | + }
|
|---|
| 3802 | + } else if (object instanceof BufferedImage) {
|
|---|
| 3803 | + // This directory is checked first when getting images
|
|---|
| 3804 | + ImageIO.write((BufferedImage) object, "png", toSave);
|
|---|
| 3805 | + }
|
|---|
| 3806 | + } catch (IOException e) {
|
|---|
| 3807 | + Logging.info(e);
|
|---|
| 3808 | + }
|
|---|
| 3809 | + }
|
|---|
| 3810 | +
|
|---|
| 3811 | + /**
|
|---|
| 3812 | + * Get the generated layer->style mapping
|
|---|
| 3813 | + * @return The mapping (use to enable/disable a paint style)
|
|---|
| 3814 | + */
|
|---|
| 3815 | + public Map<Source, ElemStyles> getSources() {
|
|---|
| 3816 | + return this.sources;
|
|---|
| 3817 | + }
|
|---|
| 3818 | +
|
|---|
| 3819 | + /**
|
|---|
| 3820 | + * Get the sprite url for the style
|
|---|
| 3821 | + * @return The base sprite url
|
|---|
| 3822 | + */
|
|---|
| 3823 | + public String getSpriteUrl() {
|
|---|
| 3824 | + return this.spriteUrl;
|
|---|
| 3825 | + }
|
|---|
| 3826 | +
|
|---|
| 3827 | + @Override
|
|---|
| 3828 | + public boolean equals(Object other) {
|
|---|
| 3829 | + if (other != null && other.getClass() == this.getClass()) {
|
|---|
| 3830 | + MapBoxVectorStyle o = (MapBoxVectorStyle) other;
|
|---|
| 3831 | + return this.version == o.version
|
|---|
| 3832 | + && Objects.equals(this.name, o.name)
|
|---|
| 3833 | + && Objects.equals(this.glyphUrl, o.glyphUrl)
|
|---|
| 3834 | + && Objects.equals(this.spriteUrl, o.spriteUrl)
|
|---|
| 3835 | + && Objects.equals(this.sources, o.sources);
|
|---|
| 3836 | + }
|
|---|
| 3837 | + return false;
|
|---|
| 3838 | + }
|
|---|
| 3839 | +
|
|---|
| 3840 | + @Override
|
|---|
| 3841 | + public int hashCode() {
|
|---|
| 3842 | + return Objects.hash(this.name, this.version, this.glyphUrl, this.spriteUrl, this.sources);
|
|---|
| 3843 | + }
|
|---|
| 3844 | +}
|
|---|
| 3845 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Scheme.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Scheme.java
|
|---|
| 3846 | new file mode 100644
|
|---|
| 3847 | index 000000000..e8583b940
|
|---|
| 3848 | --- /dev/null
|
|---|
| 3849 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Scheme.java
|
|---|
| 3850 | @@ -0,0 +1,12 @@
|
|---|
| 3851 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 3852 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 3853 | +
|
|---|
| 3854 | +/**
|
|---|
| 3855 | + * The scheme used for tiles
|
|---|
| 3856 | + */
|
|---|
| 3857 | +public enum Scheme {
|
|---|
| 3858 | + /** Standard slippy map scheme */
|
|---|
| 3859 | + XYZ,
|
|---|
| 3860 | + /** OSGeo specification scheme */
|
|---|
| 3861 | + TMS
|
|---|
| 3862 | +}
|
|---|
| 3863 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 3864 | new file mode 100644
|
|---|
| 3865 | index 000000000..dc7c62d62
|
|---|
| 3866 | --- /dev/null
|
|---|
| 3867 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 3868 | @@ -0,0 +1,254 @@
|
|---|
| 3869 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 3870 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 3871 | +
|
|---|
| 3872 | +import java.text.MessageFormat;
|
|---|
| 3873 | +import java.util.ArrayList;
|
|---|
| 3874 | +import java.util.Arrays;
|
|---|
| 3875 | +import java.util.Collection;
|
|---|
| 3876 | +import java.util.Collections;
|
|---|
| 3877 | +import java.util.List;
|
|---|
| 3878 | +import java.util.Locale;
|
|---|
| 3879 | +import java.util.Objects;
|
|---|
| 3880 | +import java.util.function.IntFunction;
|
|---|
| 3881 | +
|
|---|
| 3882 | +import javax.json.JsonArray;
|
|---|
| 3883 | +import javax.json.JsonObject;
|
|---|
| 3884 | +import javax.json.JsonString;
|
|---|
| 3885 | +import javax.json.JsonValue;
|
|---|
| 3886 | +
|
|---|
| 3887 | +import org.openstreetmap.josm.data.Bounds;
|
|---|
| 3888 | +
|
|---|
| 3889 | +/**
|
|---|
| 3890 | + * A source from a MapBox Vector Style
|
|---|
| 3891 | + *
|
|---|
| 3892 | + * @author Taylor Smock
|
|---|
| 3893 | + * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/">https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/</a>
|
|---|
| 3894 | + * @since xxx
|
|---|
| 3895 | + */
|
|---|
| 3896 | +public class Source {
|
|---|
| 3897 | + /**
|
|---|
| 3898 | + * A common function for zoom constraints
|
|---|
| 3899 | + */
|
|---|
| 3900 | + private static class ZoomBoundFunction implements IntFunction<Integer> {
|
|---|
| 3901 | + private final int min;
|
|---|
| 3902 | + private final int max;
|
|---|
| 3903 | + /**
|
|---|
| 3904 | + * Create a new bound for zooms
|
|---|
| 3905 | + * @param min The min zoom
|
|---|
| 3906 | + * @param max The max zoom
|
|---|
| 3907 | + */
|
|---|
| 3908 | + ZoomBoundFunction(int min, int max) {
|
|---|
| 3909 | + this.min = min;
|
|---|
| 3910 | + this.max = max;
|
|---|
| 3911 | + }
|
|---|
| 3912 | +
|
|---|
| 3913 | + @Override public Integer apply(int value) {
|
|---|
| 3914 | + return Math.max(min, Math.min(value, max));
|
|---|
| 3915 | + }
|
|---|
| 3916 | + }
|
|---|
| 3917 | +
|
|---|
| 3918 | + /**
|
|---|
| 3919 | + * WMS servers should contain a "{bbox-epsg-3857}" parameter for the bbox
|
|---|
| 3920 | + */
|
|---|
| 3921 | + private static final String WMS_BBOX = "bbox-epsg-3857";
|
|---|
| 3922 | +
|
|---|
| 3923 | + private static final String[] NO_URLS = new String[0];
|
|---|
| 3924 | +
|
|---|
| 3925 | + /**
|
|---|
| 3926 | + * Constrain the min/max zooms to be between 0 and 30, as per tilejson spec
|
|---|
| 3927 | + */
|
|---|
| 3928 | + private static final IntFunction<Integer> ZOOM_BOUND_FUNCTION = new ZoomBoundFunction(0, 30);
|
|---|
| 3929 | +
|
|---|
| 3930 | + /* Common items */
|
|---|
| 3931 | + /**
|
|---|
| 3932 | + * The name of the source
|
|---|
| 3933 | + */
|
|---|
| 3934 | + private final String name;
|
|---|
| 3935 | + /**
|
|---|
| 3936 | + * The type of the source
|
|---|
| 3937 | + */
|
|---|
| 3938 | + private final SourceType sourceType;
|
|---|
| 3939 | +
|
|---|
| 3940 | + /* Common tiled data */
|
|---|
| 3941 | + /**
|
|---|
| 3942 | + * The minimum zoom supported
|
|---|
| 3943 | + */
|
|---|
| 3944 | + private final int minZoom;
|
|---|
| 3945 | + /**
|
|---|
| 3946 | + * The maximum zoom supported
|
|---|
| 3947 | + */
|
|---|
| 3948 | + private final int maxZoom;
|
|---|
| 3949 | + /**
|
|---|
| 3950 | + * The tile urls. These usually have replaceable fields.
|
|---|
| 3951 | + */
|
|---|
| 3952 | + private final String[] tileUrls;
|
|---|
| 3953 | +
|
|---|
| 3954 | + /* Vector and raster data */
|
|---|
| 3955 | + /**
|
|---|
| 3956 | + * The attribution to display for the user
|
|---|
| 3957 | + */
|
|---|
| 3958 | + private final String attribution;
|
|---|
| 3959 | + /**
|
|---|
| 3960 | + * The bounds of the data. We should not request data outside of the bounds
|
|---|
| 3961 | + */
|
|---|
| 3962 | + private final Bounds bounds;
|
|---|
| 3963 | + /**
|
|---|
| 3964 | + * The property to use as a feature id. Can be parameterized
|
|---|
| 3965 | + */
|
|---|
| 3966 | + private final String promoteId;
|
|---|
| 3967 | + /**
|
|---|
| 3968 | + * The tile scheme
|
|---|
| 3969 | + */
|
|---|
| 3970 | + private final Scheme scheme;
|
|---|
| 3971 | + /**
|
|---|
| 3972 | + * {@code true} if the tiles should not be cached
|
|---|
| 3973 | + */
|
|---|
| 3974 | + private final boolean volatileCache;
|
|---|
| 3975 | +
|
|---|
| 3976 | + /* Raster data */
|
|---|
| 3977 | + /**
|
|---|
| 3978 | + * The tile size
|
|---|
| 3979 | + */
|
|---|
| 3980 | + private final int tileSize;
|
|---|
| 3981 | +
|
|---|
| 3982 | + /**
|
|---|
| 3983 | + * Create a new Source object
|
|---|
| 3984 | + *
|
|---|
| 3985 | + * @param name The name of the source object
|
|---|
| 3986 | + * @param data The data to set the source information with
|
|---|
| 3987 | + */
|
|---|
| 3988 | + public Source(final String name, final JsonObject data) {
|
|---|
| 3989 | + Objects.requireNonNull(name, "Name cannot be null");
|
|---|
| 3990 | + Objects.requireNonNull(data, "Data cannot be null");
|
|---|
| 3991 | + this.name = name;
|
|---|
| 3992 | + // "type" is required (so throw an NPE if it doesn't exist)
|
|---|
| 3993 | + final String type = data.getString("type");
|
|---|
| 3994 | + this.sourceType = SourceType.valueOf(type.replace("-", "_").toUpperCase(Locale.ROOT));
|
|---|
| 3995 | + // This can also contain SourceType.RASTER_DEM (only needs encoding)
|
|---|
| 3996 | + if (SourceType.VECTOR == this.sourceType || SourceType.RASTER == this.sourceType) {
|
|---|
| 3997 | + if (data.containsKey("url")) {
|
|---|
| 3998 | + // TODO implement https://github.com/mapbox/tilejson-spec
|
|---|
| 3999 | + throw new UnsupportedOperationException();
|
|---|
| 4000 | + } else {
|
|---|
| 4001 | + this.minZoom = ZOOM_BOUND_FUNCTION.apply(data.getInt("minzoom", 0));
|
|---|
| 4002 | + this.maxZoom = ZOOM_BOUND_FUNCTION.apply(data.getInt("maxzoom", 22));
|
|---|
| 4003 | + this.attribution = data.getString("attribution", null);
|
|---|
| 4004 | + if (data.containsKey("bounds") && data.get("bounds").getValueType() == JsonValue.ValueType.ARRAY) {
|
|---|
| 4005 | + final JsonArray bJsonArray = data.getJsonArray("bounds");
|
|---|
| 4006 | + if (bJsonArray.size() != 4) {
|
|---|
| 4007 | + throw new IllegalArgumentException(MessageFormat.format("bounds must have four values, but has {0}", bJsonArray.size()));
|
|---|
| 4008 | + }
|
|---|
| 4009 | + final double[] bArray = new double[bJsonArray.size()];
|
|---|
| 4010 | + for (int i = 0; i < bJsonArray.size(); i++) {
|
|---|
| 4011 | + bArray[i] = bJsonArray.getJsonNumber(i).doubleValue();
|
|---|
| 4012 | + }
|
|---|
| 4013 | + // The order in the response is
|
|---|
| 4014 | + // [south-west longitude, south-west latitude, north-east longitude, north-east latitude]
|
|---|
| 4015 | + this.bounds = new Bounds(bArray[1], bArray[0], bArray[3], bArray[2]);
|
|---|
| 4016 | + } else {
|
|---|
| 4017 | + // Don't use a static instance for bounds, as it is not a immutable class
|
|---|
| 4018 | + this.bounds = new Bounds(-85.051129, -180, 85.051129, 180);
|
|---|
| 4019 | + }
|
|---|
| 4020 | + this.promoteId = data.getString("promoteId", null);
|
|---|
| 4021 | + this.scheme = Scheme.valueOf(data.getString("scheme", "xyz").toUpperCase(Locale.ROOT));
|
|---|
| 4022 | + if (data.containsKey("tiles") && data.get("tiles").getValueType() == JsonValue.ValueType.ARRAY) {
|
|---|
| 4023 | + this.tileUrls = data.getJsonArray("tiles").stream().filter(JsonString.class::isInstance)
|
|---|
| 4024 | + .map(JsonString.class::cast).map(JsonString::getString)
|
|---|
| 4025 | + // Replace bbox-epsg-3857 with bbox (already encased with {})
|
|---|
| 4026 | + .map(url -> url.replace(WMS_BBOX, "bbox")).toArray(String[]::new);
|
|---|
| 4027 | + } else {
|
|---|
| 4028 | + this.tileUrls = NO_URLS;
|
|---|
| 4029 | + }
|
|---|
| 4030 | + this.volatileCache = data.getBoolean("volatile", false);
|
|---|
| 4031 | + this.tileSize = data.getInt("tileSize", 512);
|
|---|
| 4032 | + }
|
|---|
| 4033 | + } else {
|
|---|
| 4034 | + throw new UnsupportedOperationException();
|
|---|
| 4035 | + }
|
|---|
| 4036 | + }
|
|---|
| 4037 | +
|
|---|
| 4038 | + /**
|
|---|
| 4039 | + * Get the bounds for this source
|
|---|
| 4040 | + * @return The bounds where this source can be used
|
|---|
| 4041 | + */
|
|---|
| 4042 | + public Bounds getBounds() {
|
|---|
| 4043 | + return this.bounds;
|
|---|
| 4044 | + }
|
|---|
| 4045 | +
|
|---|
| 4046 | + /**
|
|---|
| 4047 | + * Get the source name
|
|---|
| 4048 | + * @return the name
|
|---|
| 4049 | + */
|
|---|
| 4050 | + public String getName() {
|
|---|
| 4051 | + return name;
|
|---|
| 4052 | + }
|
|---|
| 4053 | +
|
|---|
| 4054 | + /**
|
|---|
| 4055 | + * Get the URLs that can be used to get vector data
|
|---|
| 4056 | + *
|
|---|
| 4057 | + * @return The urls
|
|---|
| 4058 | + */
|
|---|
| 4059 | + public List<String> getUrls() {
|
|---|
| 4060 | + return Collections.unmodifiableList(Arrays.asList(this.tileUrls));
|
|---|
| 4061 | + }
|
|---|
| 4062 | +
|
|---|
| 4063 | + /**
|
|---|
| 4064 | + * Get the minimum zoom
|
|---|
| 4065 | + *
|
|---|
| 4066 | + * @return The min zoom (default {@code 0})
|
|---|
| 4067 | + */
|
|---|
| 4068 | + public int getMinZoom() {
|
|---|
| 4069 | + return this.minZoom;
|
|---|
| 4070 | + }
|
|---|
| 4071 | +
|
|---|
| 4072 | + /**
|
|---|
| 4073 | + * Get the max zoom
|
|---|
| 4074 | + *
|
|---|
| 4075 | + * @return The max zoom (default {@code 22})
|
|---|
| 4076 | + */
|
|---|
| 4077 | + public int getMaxZoom() {
|
|---|
| 4078 | + return this.maxZoom;
|
|---|
| 4079 | + }
|
|---|
| 4080 | +
|
|---|
| 4081 | + /**
|
|---|
| 4082 | + * Get the attribution for this source
|
|---|
| 4083 | + *
|
|---|
| 4084 | + * @return The attribution text. May be {@code null}.
|
|---|
| 4085 | + */
|
|---|
| 4086 | + public String getAttributionText() {
|
|---|
| 4087 | + return this.attribution;
|
|---|
| 4088 | + }
|
|---|
| 4089 | +
|
|---|
| 4090 | + @Override
|
|---|
| 4091 | + public String toString() {
|
|---|
| 4092 | + Collection<String> parts = new ArrayList<>(1 + this.getUrls().size());
|
|---|
| 4093 | + parts.add(this.getName());
|
|---|
| 4094 | + parts.addAll(this.getUrls());
|
|---|
| 4095 | + return String.join(" ", parts);
|
|---|
| 4096 | + }
|
|---|
| 4097 | +
|
|---|
| 4098 | + @Override
|
|---|
| 4099 | + public boolean equals(Object other) {
|
|---|
| 4100 | + if (other != null && this.getClass() == other.getClass()) {
|
|---|
| 4101 | + Source o = (Source) other;
|
|---|
| 4102 | + return Objects.equals(this.name, o.name)
|
|---|
| 4103 | + && this.sourceType == o.sourceType
|
|---|
| 4104 | + && this.minZoom == o.minZoom
|
|---|
| 4105 | + && this.maxZoom == o.maxZoom
|
|---|
| 4106 | + && Objects.equals(this.attribution, o.attribution)
|
|---|
| 4107 | + && Objects.equals(this.promoteId, o.promoteId)
|
|---|
| 4108 | + && this.scheme == o.scheme
|
|---|
| 4109 | + && this.volatileCache == o.volatileCache
|
|---|
| 4110 | + && this.tileSize == o.tileSize
|
|---|
| 4111 | + && Objects.equals(this.bounds, o.bounds)
|
|---|
| 4112 | + && Objects.deepEquals(this.tileUrls, o.tileUrls);
|
|---|
| 4113 | + }
|
|---|
| 4114 | + return false;
|
|---|
| 4115 | + }
|
|---|
| 4116 | +
|
|---|
| 4117 | + @Override
|
|---|
| 4118 | + public int hashCode() {
|
|---|
| 4119 | + return Objects.hash(this.name, this.sourceType, this.minZoom, this.maxZoom, this.attribution, this.promoteId,
|
|---|
| 4120 | + this.scheme, this.volatileCache, this.tileSize, this.bounds, Arrays.hashCode(this.tileUrls));
|
|---|
| 4121 | + }
|
|---|
| 4122 | +}
|
|---|
| 4123 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java
|
|---|
| 4124 | new file mode 100644
|
|---|
| 4125 | index 000000000..a086289d6
|
|---|
| 4126 | --- /dev/null
|
|---|
| 4127 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java
|
|---|
| 4128 | @@ -0,0 +1,17 @@
|
|---|
| 4129 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 4130 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 4131 | +
|
|---|
| 4132 | +/**
|
|---|
| 4133 | + * The "source type" for the data (MapBox Vector Style specification)
|
|---|
| 4134 | + *
|
|---|
| 4135 | + * @author Taylor Smock
|
|---|
| 4136 | + * @since xxx
|
|---|
| 4137 | + */
|
|---|
| 4138 | +public enum SourceType {
|
|---|
| 4139 | + VECTOR,
|
|---|
| 4140 | + RASTER,
|
|---|
| 4141 | + RASTER_DEM,
|
|---|
| 4142 | + GEOJSON,
|
|---|
| 4143 | + IMAGE,
|
|---|
| 4144 | + VIDEO
|
|---|
| 4145 | +}
|
|---|
| 4146 | diff --git a/src/org/openstreetmap/josm/data/osm/IPrimitive.java b/src/org/openstreetmap/josm/data/osm/IPrimitive.java
|
|---|
| 4147 | index cdabcd1b6..34e2ca0be 100644
|
|---|
| 4148 | --- a/src/org/openstreetmap/josm/data/osm/IPrimitive.java
|
|---|
| 4149 | +++ b/src/org/openstreetmap/josm/data/osm/IPrimitive.java
|
|---|
| 4150 | @@ -391,6 +391,15 @@ public interface IPrimitive extends IQuadBucketType, Tagged, PrimitiveId, Stylab
|
|---|
| 4151 | return getName();
|
|---|
| 4152 | }
|
|---|
| 4153 |
|
|---|
| 4154 | + /**
|
|---|
| 4155 | + * Get an object to synchronize the style cache on. This <i>should</i> be a field that does not change during paint.
|
|---|
| 4156 | + * By default, it returns the current object, but should be overriden to avoid some performance issues.
|
|---|
| 4157 | + * @return A non-{@code null} object to synchronize on when painting
|
|---|
| 4158 | + */
|
|---|
| 4159 | + default Object getStyleCacheSyncObject() {
|
|---|
| 4160 | + return this;
|
|---|
| 4161 | + }
|
|---|
| 4162 | +
|
|---|
| 4163 | /**
|
|---|
| 4164 | * Replies the display name of a primitive formatted by <code>formatter</code>
|
|---|
| 4165 | * @param formatter formatter to use
|
|---|
| 4166 | diff --git a/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java b/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java
|
|---|
| 4167 | index fef095ea1..970635d96 100644
|
|---|
| 4168 | --- a/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java
|
|---|
| 4169 | +++ b/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java
|
|---|
| 4170 | @@ -85,6 +85,17 @@ public class ElemStyles implements PreferenceChangedListener {
|
|---|
| 4171 | Config.getPref().addPreferenceChangeListener(this);
|
|---|
| 4172 | }
|
|---|
| 4173 |
|
|---|
| 4174 | + /**
|
|---|
| 4175 | + * Constructs a new {@code ElemStyles} with specific style sources. This does not listen to preference changes,
|
|---|
| 4176 | + * and therefore should only be used with layers that have specific drawing requirements.
|
|---|
| 4177 | + *
|
|---|
| 4178 | + * @param sources The style sources (these cannot be added to, or removed from)
|
|---|
| 4179 | + * @since xxx
|
|---|
| 4180 | + */
|
|---|
| 4181 | + public ElemStyles(Collection<StyleSource> sources) {
|
|---|
| 4182 | + this.styleSources.addAll(sources);
|
|---|
| 4183 | + }
|
|---|
| 4184 | +
|
|---|
| 4185 | /**
|
|---|
| 4186 | * Clear the style cache for all primitives of all DataSets.
|
|---|
| 4187 | */
|
|---|
| 4188 | @@ -151,69 +162,71 @@ public class ElemStyles implements PreferenceChangedListener {
|
|---|
| 4189 | * @since 13810 (signature)
|
|---|
| 4190 | */
|
|---|
| 4191 | public Pair<StyleElementList, Range> getStyleCacheWithRange(IPrimitive osm, double scale, NavigatableComponent nc) {
|
|---|
| 4192 | - if (!osm.isCachedStyleUpToDate() || scale <= 0) {
|
|---|
| 4193 | - osm.setCachedStyle(StyleCache.EMPTY_STYLECACHE);
|
|---|
| 4194 | - } else {
|
|---|
| 4195 | - Pair<StyleElementList, Range> lst = osm.getCachedStyle().getWithRange(scale, osm.isSelected());
|
|---|
| 4196 | - if (lst.a != null)
|
|---|
| 4197 | - return lst;
|
|---|
| 4198 | - }
|
|---|
| 4199 | - Pair<StyleElementList, Range> p = getImpl(osm, scale, nc);
|
|---|
| 4200 | - if (osm instanceof INode && isDefaultNodes()) {
|
|---|
| 4201 | - if (p.a.isEmpty()) {
|
|---|
| 4202 | - if (TextLabel.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
|
|---|
| 4203 | - p.a = DefaultStyles.DEFAULT_NODE_STYLELIST_TEXT;
|
|---|
| 4204 | - } else {
|
|---|
| 4205 | - p.a = DefaultStyles.DEFAULT_NODE_STYLELIST;
|
|---|
| 4206 | - }
|
|---|
| 4207 | + synchronized (osm.getStyleCacheSyncObject()) {
|
|---|
| 4208 | + if (!osm.isCachedStyleUpToDate() || scale <= 0) {
|
|---|
| 4209 | + osm.setCachedStyle(StyleCache.EMPTY_STYLECACHE);
|
|---|
| 4210 | } else {
|
|---|
| 4211 | - boolean hasNonModifier = false;
|
|---|
| 4212 | - boolean hasText = false;
|
|---|
| 4213 | - for (StyleElement s : p.a) {
|
|---|
| 4214 | - if (s instanceof BoxTextElement) {
|
|---|
| 4215 | - hasText = true;
|
|---|
| 4216 | + Pair<StyleElementList, Range> lst = osm.getCachedStyle().getWithRange(scale, osm.isSelected());
|
|---|
| 4217 | + if (lst.a != null)
|
|---|
| 4218 | + return lst;
|
|---|
| 4219 | + }
|
|---|
| 4220 | + Pair<StyleElementList, Range> p = getImpl(osm, scale, nc);
|
|---|
| 4221 | + if (osm instanceof INode && isDefaultNodes()) {
|
|---|
| 4222 | + if (p.a.isEmpty()) {
|
|---|
| 4223 | + if (TextLabel.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
|
|---|
| 4224 | + p.a = DefaultStyles.DEFAULT_NODE_STYLELIST_TEXT;
|
|---|
| 4225 | } else {
|
|---|
| 4226 | - if (!s.isModifier) {
|
|---|
| 4227 | - hasNonModifier = true;
|
|---|
| 4228 | + p.a = DefaultStyles.DEFAULT_NODE_STYLELIST;
|
|---|
| 4229 | + }
|
|---|
| 4230 | + } else {
|
|---|
| 4231 | + boolean hasNonModifier = false;
|
|---|
| 4232 | + boolean hasText = false;
|
|---|
| 4233 | + for (StyleElement s : p.a) {
|
|---|
| 4234 | + if (s instanceof BoxTextElement) {
|
|---|
| 4235 | + hasText = true;
|
|---|
| 4236 | + } else {
|
|---|
| 4237 | + if (!s.isModifier) {
|
|---|
| 4238 | + hasNonModifier = true;
|
|---|
| 4239 | + }
|
|---|
| 4240 | }
|
|---|
| 4241 | }
|
|---|
| 4242 | - }
|
|---|
| 4243 | - if (!hasNonModifier) {
|
|---|
| 4244 | - p.a = new StyleElementList(p.a, DefaultStyles.SIMPLE_NODE_ELEMSTYLE);
|
|---|
| 4245 | - if (!hasText && TextLabel.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
|
|---|
| 4246 | - p.a = new StyleElementList(p.a, DefaultStyles.SIMPLE_NODE_TEXT_ELEMSTYLE);
|
|---|
| 4247 | + if (!hasNonModifier) {
|
|---|
| 4248 | + p.a = new StyleElementList(p.a, DefaultStyles.SIMPLE_NODE_ELEMSTYLE);
|
|---|
| 4249 | + if (!hasText && TextLabel.AUTO_LABEL_COMPOSITION_STRATEGY.compose(osm) != null) {
|
|---|
| 4250 | + p.a = new StyleElementList(p.a, DefaultStyles.SIMPLE_NODE_TEXT_ELEMSTYLE);
|
|---|
| 4251 | + }
|
|---|
| 4252 | }
|
|---|
| 4253 | }
|
|---|
| 4254 | - }
|
|---|
| 4255 | - } else if (osm instanceof IWay && isDefaultLines()) {
|
|---|
| 4256 | - boolean hasProperLineStyle = false;
|
|---|
| 4257 | - for (StyleElement s : p.a) {
|
|---|
| 4258 | - if (s.isProperLineStyle()) {
|
|---|
| 4259 | - hasProperLineStyle = true;
|
|---|
| 4260 | - break;
|
|---|
| 4261 | - }
|
|---|
| 4262 | - }
|
|---|
| 4263 | - if (!hasProperLineStyle) {
|
|---|
| 4264 | - LineElement line = LineElement.UNTAGGED_WAY;
|
|---|
| 4265 | - for (StyleElement element : p.a) {
|
|---|
| 4266 | - if (element instanceof AreaElement) {
|
|---|
| 4267 | - line = LineElement.createSimpleLineStyle(((AreaElement) element).color, true);
|
|---|
| 4268 | + } else if (osm instanceof IWay && isDefaultLines()) {
|
|---|
| 4269 | + boolean hasProperLineStyle = false;
|
|---|
| 4270 | + for (StyleElement s : p.a) {
|
|---|
| 4271 | + if (s.isProperLineStyle()) {
|
|---|
| 4272 | + hasProperLineStyle = true;
|
|---|
| 4273 | break;
|
|---|
| 4274 | }
|
|---|
| 4275 | }
|
|---|
| 4276 | - p.a = new StyleElementList(p.a, line);
|
|---|
| 4277 | + if (!hasProperLineStyle) {
|
|---|
| 4278 | + LineElement line = LineElement.UNTAGGED_WAY;
|
|---|
| 4279 | + for (StyleElement element : p.a) {
|
|---|
| 4280 | + if (element instanceof AreaElement) {
|
|---|
| 4281 | + line = LineElement.createSimpleLineStyle(((AreaElement) element).color, true);
|
|---|
| 4282 | + break;
|
|---|
| 4283 | + }
|
|---|
| 4284 | + }
|
|---|
| 4285 | + p.a = new StyleElementList(p.a, line);
|
|---|
| 4286 | + }
|
|---|
| 4287 | }
|
|---|
| 4288 | + StyleCache style = osm.getCachedStyle() != null ? osm.getCachedStyle() : StyleCache.EMPTY_STYLECACHE;
|
|---|
| 4289 | + try {
|
|---|
| 4290 | + osm.setCachedStyle(style.put(p.a, p.b, osm.isSelected()));
|
|---|
| 4291 | + } catch (RangeViolatedError e) {
|
|---|
| 4292 | + throw new AssertionError("Range violated: " + e.getMessage()
|
|---|
| 4293 | + + " (object: " + osm.getPrimitiveId() + ", current style: " + osm.getCachedStyle()
|
|---|
| 4294 | + + ", scale: " + scale + ", new stylelist: " + p.a + ", new range: " + p.b + ')', e);
|
|---|
| 4295 | + }
|
|---|
| 4296 | + osm.declareCachedStyleUpToDate();
|
|---|
| 4297 | + return p;
|
|---|
| 4298 | }
|
|---|
| 4299 | - StyleCache style = osm.getCachedStyle() != null ? osm.getCachedStyle() : StyleCache.EMPTY_STYLECACHE;
|
|---|
| 4300 | - try {
|
|---|
| 4301 | - osm.setCachedStyle(style.put(p.a, p.b, osm.isSelected()));
|
|---|
| 4302 | - } catch (RangeViolatedError e) {
|
|---|
| 4303 | - throw new AssertionError("Range violated: " + e.getMessage()
|
|---|
| 4304 | - + " (object: " + osm.getPrimitiveId() + ", current style: "+osm.getCachedStyle()
|
|---|
| 4305 | - + ", scale: " + scale + ", new stylelist: " + p.a + ", new range: " + p.b + ')', e);
|
|---|
| 4306 | - }
|
|---|
| 4307 | - osm.declareCachedStyleUpToDate();
|
|---|
| 4308 | - return p;
|
|---|
| 4309 | }
|
|---|
| 4310 |
|
|---|
| 4311 | /**
|
|---|
| 4312 | @@ -376,7 +389,6 @@ public class ElemStyles implements PreferenceChangedListener {
|
|---|
| 4313 | * @since 13810 (signature)
|
|---|
| 4314 | */
|
|---|
| 4315 | public Pair<StyleElementList, Range> generateStyles(IPrimitive osm, double scale, boolean pretendWayIsClosed) {
|
|---|
| 4316 | -
|
|---|
| 4317 | List<StyleElement> sl = new ArrayList<>();
|
|---|
| 4318 | MultiCascade mc = new MultiCascade();
|
|---|
| 4319 | Environment env = new Environment(osm, mc, null, null);
|
|---|
| 4320 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/ExpressionTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/ExpressionTest.java
|
|---|
| 4321 | new file mode 100644
|
|---|
| 4322 | index 000000000..0130da35e
|
|---|
| 4323 | --- /dev/null
|
|---|
| 4324 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/ExpressionTest.java
|
|---|
| 4325 | @@ -0,0 +1,53 @@
|
|---|
| 4326 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 4327 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 4328 | +
|
|---|
| 4329 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 4330 | +
|
|---|
| 4331 | +
|
|---|
| 4332 | +import javax.json.Json;
|
|---|
| 4333 | +import javax.json.JsonValue;
|
|---|
| 4334 | +
|
|---|
| 4335 | +import nl.jqno.equalsverifier.EqualsVerifier;
|
|---|
| 4336 | +import org.junit.jupiter.api.Test;
|
|---|
| 4337 | +
|
|---|
| 4338 | +/**
|
|---|
| 4339 | + * Test class for {@link Expression}
|
|---|
| 4340 | + * @author Taylor Smock
|
|---|
| 4341 | + * @since xxx
|
|---|
| 4342 | + */
|
|---|
| 4343 | +class ExpressionTest {
|
|---|
| 4344 | + @Test
|
|---|
| 4345 | + void testInvalidJson() {
|
|---|
| 4346 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(JsonValue.NULL));
|
|---|
| 4347 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(JsonValue.FALSE));
|
|---|
| 4348 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(JsonValue.TRUE));
|
|---|
| 4349 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(JsonValue.EMPTY_JSON_OBJECT));
|
|---|
| 4350 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(JsonValue.EMPTY_JSON_ARRAY));
|
|---|
| 4351 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(Json.createObjectBuilder().add("bad", "value").build()));
|
|---|
| 4352 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(Json.createValue(1)));
|
|---|
| 4353 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(Json.createValue(1.0)));
|
|---|
| 4354 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(Json.createValue("bad string")));
|
|---|
| 4355 | + }
|
|---|
| 4356 | +
|
|---|
| 4357 | + @Test
|
|---|
| 4358 | + void testBasicExpressions() {
|
|---|
| 4359 | + // "filter": [ "==|>=|<=|<|>", "key", "value" ]
|
|---|
| 4360 | + assertEquals("[key=value]", new Expression(Json.createArrayBuilder().add("==").add("key").add("value").build()).toString());
|
|---|
| 4361 | + assertEquals("[key>=true]", new Expression(Json.createArrayBuilder().add(">=").add("key").add(true).build()).toString());
|
|---|
| 4362 | + assertEquals("[key<=false]", new Expression(Json.createArrayBuilder().add("<=").add("key").add(false).build()).toString());
|
|---|
| 4363 | + assertEquals("[key<1]", new Expression(Json.createArrayBuilder().add("<").add("key").add(1).build()).toString());
|
|---|
| 4364 | + assertEquals("[key>2.5]", new Expression(Json.createArrayBuilder().add(">").add("key").add(2.5).build()).toString());
|
|---|
| 4365 | + // Test bad expression
|
|---|
| 4366 | + assertEquals(Expression.EMPTY_EXPRESSION, new Expression(Json.createArrayBuilder().add(">>").add("key").add("value").build()));
|
|---|
| 4367 | +
|
|---|
| 4368 | + // Test expressions with a subarray and object. This is expected to fail when properly supported, so it should be fixed.
|
|---|
| 4369 | + assertEquals("[key=[{bad:value}]]", new Expression(Json.createArrayBuilder().add("==").add("key").add(
|
|---|
| 4370 | + Json.createArrayBuilder().add(Json.createObjectBuilder().add("bad", "value"))).build()).toString());
|
|---|
| 4371 | + assertEquals("[key=]", new Expression(Json.createArrayBuilder().add("==").add("key").add(JsonValue.NULL).build()).toString());
|
|---|
| 4372 | + }
|
|---|
| 4373 | +
|
|---|
| 4374 | + @Test
|
|---|
| 4375 | + void testEquals() {
|
|---|
| 4376 | + EqualsVerifier.forClass(Expression.class).verify();
|
|---|
| 4377 | + }
|
|---|
| 4378 | +}
|
|---|
| 4379 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 4380 | new file mode 100644
|
|---|
| 4381 | index 000000000..28b09b950
|
|---|
| 4382 | --- /dev/null
|
|---|
| 4383 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 4384 | @@ -0,0 +1,601 @@
|
|---|
| 4385 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 4386 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 4387 | +
|
|---|
| 4388 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 4389 | +import static org.junit.jupiter.api.Assertions.assertNull;
|
|---|
| 4390 | +import static org.junit.jupiter.api.Assertions.assertSame;
|
|---|
| 4391 | +import static org.junit.jupiter.api.Assertions.assertThrows;
|
|---|
| 4392 | +
|
|---|
| 4393 | +import java.text.MessageFormat;
|
|---|
| 4394 | +import java.util.Locale;
|
|---|
| 4395 | +
|
|---|
| 4396 | +import javax.json.Json;
|
|---|
| 4397 | +import javax.json.JsonObject;
|
|---|
| 4398 | +import javax.json.JsonValue;
|
|---|
| 4399 | +
|
|---|
| 4400 | +import nl.jqno.equalsverifier.EqualsVerifier;
|
|---|
| 4401 | +import org.junit.jupiter.api.Test;
|
|---|
| 4402 | +
|
|---|
| 4403 | +/**
|
|---|
| 4404 | + * Test class for {@link Layers}.
|
|---|
| 4405 | + * @implNote Tests will fail when support is added for new styling information.
|
|---|
| 4406 | + * All current (2021-03-31) properties are checked for in some form or another.
|
|---|
| 4407 | + * @author Taylor Smock
|
|---|
| 4408 | + * @since xxx
|
|---|
| 4409 | + */
|
|---|
| 4410 | +class LayersTest {
|
|---|
| 4411 | + @Test
|
|---|
| 4412 | + void testBackground() {
|
|---|
| 4413 | + // Test an empty background layer
|
|---|
| 4414 | + Layers emptyBackgroundLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4415 | + .add("type", Layers.Type.BACKGROUND.name())
|
|---|
| 4416 | + .add("id", "Empty Background").build());
|
|---|
| 4417 | + assertEquals("Empty Background", emptyBackgroundLayer.getId());
|
|---|
| 4418 | + assertEquals(Layers.Type.BACKGROUND, emptyBackgroundLayer.getType());
|
|---|
| 4419 | + assertNull(emptyBackgroundLayer.getSource());
|
|---|
| 4420 | + assertSame(Expression.EMPTY_EXPRESSION, emptyBackgroundLayer.getFilter());
|
|---|
| 4421 | + assertEquals("", emptyBackgroundLayer.toString());
|
|---|
| 4422 | +
|
|---|
| 4423 | + // Test a background layer with some styling information
|
|---|
| 4424 | + JsonObject allProperties = Json.createObjectBuilder()
|
|---|
| 4425 | + .add("background-color", "#fff000") // fill-color:#fff000;
|
|---|
| 4426 | + .add("background-opacity", 0.5) // No good mapping for JOSM yet
|
|---|
| 4427 | + .add("background-pattern", "null") // This should be an image, not implemented
|
|---|
| 4428 | + .build();
|
|---|
| 4429 | + Layers backgroundLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4430 | + .add("id", "Background layer")
|
|---|
| 4431 | + .add("type", Layers.Type.BACKGROUND.name().toLowerCase(Locale.ROOT))
|
|---|
| 4432 | + .add("paint", allProperties)
|
|---|
| 4433 | + .build());
|
|---|
| 4434 | + assertEquals("canvas{fill-color:#fff000;}", backgroundLayer.toString());
|
|---|
| 4435 | +
|
|---|
| 4436 | + // Test a background layer with some styling information, but invisible
|
|---|
| 4437 | + Layers invisibleBackgroundLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4438 | + .add("id", "Background layer")
|
|---|
| 4439 | + .add("type", Layers.Type.BACKGROUND.name().toLowerCase(Locale.ROOT))
|
|---|
| 4440 | + .add("layout", Json.createObjectBuilder().add("visibility", "none").build())
|
|---|
| 4441 | + .add("paint", allProperties).build());
|
|---|
| 4442 | + assertEquals("", invisibleBackgroundLayer.toString());
|
|---|
| 4443 | + }
|
|---|
| 4444 | +
|
|---|
| 4445 | + @Test
|
|---|
| 4446 | + void testFill() {
|
|---|
| 4447 | + // Test a layer without a source (should fail)
|
|---|
| 4448 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4449 | + .add("type", Layers.Type.FILL.name())
|
|---|
| 4450 | + .add("id", "Empty Fill").build()));
|
|---|
| 4451 | +
|
|---|
| 4452 | + // Test an empty fill layer
|
|---|
| 4453 | + Layers emptyFillLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4454 | + .add("type", Layers.Type.FILL.name())
|
|---|
| 4455 | + .add("id", "Empty Fill")
|
|---|
| 4456 | + .add("source", "Random source").build());
|
|---|
| 4457 | + assertEquals("Empty Fill", emptyFillLayer.getId());
|
|---|
| 4458 | + assertEquals("Random source", emptyFillLayer.getSource());
|
|---|
| 4459 | + assertEquals("", emptyFillLayer.toString());
|
|---|
| 4460 | +
|
|---|
| 4461 | + // Test a fully implemented fill layer
|
|---|
| 4462 | + JsonObject allLayoutProperties = Json.createObjectBuilder()
|
|---|
| 4463 | + .add("fill-sort-key", 5)
|
|---|
| 4464 | + .add("visibility", "visible")
|
|---|
| 4465 | + .build();
|
|---|
| 4466 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4467 | + .add("fill-antialias", false)
|
|---|
| 4468 | + .add("fill-color", "#fff000") // fill-color:#fff000
|
|---|
| 4469 | + .add("fill-opacity", 0.5) // fill-opacity:0.5
|
|---|
| 4470 | + .add("fill-outline-color", "#ffff00") // fill-color:#ffff00 (defaults to fill-color)
|
|---|
| 4471 | + .add("fill-pattern", JsonValue.NULL) // disables fill-outline-color and fill-color
|
|---|
| 4472 | + .add("fill-translate", Json.createArrayBuilder().add(5).add(5))
|
|---|
| 4473 | + .add("fill-translate-anchor", "viewport") // requires fill-translate
|
|---|
| 4474 | + .build();
|
|---|
| 4475 | +
|
|---|
| 4476 | + Layers fullFillLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4477 | + .add("type", Layers.Type.FILL.toString())
|
|---|
| 4478 | + .add("id", "random-layer-id")
|
|---|
| 4479 | + .add("source", "Random source")
|
|---|
| 4480 | + .add("layout", allLayoutProperties)
|
|---|
| 4481 | + .add("paint", allPaintProperties)
|
|---|
| 4482 | + .build());
|
|---|
| 4483 | + assertEquals("random-layer-id", fullFillLayer.getId());
|
|---|
| 4484 | + assertEquals(Layers.Type.FILL, fullFillLayer.getType());
|
|---|
| 4485 | + assertEquals("area::random-layer-id{fill-color:#fff000;fill-opacity:0.5;color:#ffff00;}", fullFillLayer.toString());
|
|---|
| 4486 | +
|
|---|
| 4487 | + // Test a fully implemented fill layer (invisible)
|
|---|
| 4488 | + Layers fullFillInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4489 | + .add("type", Layers.Type.FILL.toString())
|
|---|
| 4490 | + .add("id", "random-layer-id")
|
|---|
| 4491 | + .add("source", "Random source")
|
|---|
| 4492 | + .add("layout", Json.createObjectBuilder(allLayoutProperties)
|
|---|
| 4493 | + .add("visibility", "none"))
|
|---|
| 4494 | + .add("paint", allPaintProperties)
|
|---|
| 4495 | + .build());
|
|---|
| 4496 | + assertEquals("random-layer-id", fullFillInvisibleLayer.getId());
|
|---|
| 4497 | + assertEquals(Layers.Type.FILL, fullFillInvisibleLayer.getType());
|
|---|
| 4498 | + assertEquals("", fullFillInvisibleLayer.toString());
|
|---|
| 4499 | + }
|
|---|
| 4500 | +
|
|---|
| 4501 | + @Test
|
|---|
| 4502 | + void testLine() {
|
|---|
| 4503 | + // Test a layer without a source (should fail)
|
|---|
| 4504 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4505 | + .add("type", Layers.Type.LINE.name())
|
|---|
| 4506 | + .add("id", "Empty Line").build()));
|
|---|
| 4507 | +
|
|---|
| 4508 | + JsonObject allLayoutProperties = Json.createObjectBuilder()
|
|---|
| 4509 | + .add("line-cap", "round") // linecap:round;
|
|---|
| 4510 | + .add("line-join", "bevel")
|
|---|
| 4511 | + .add("line-miter-limit", 65)
|
|---|
| 4512 | + .add("line-round-limit", 1.5)
|
|---|
| 4513 | + .add("line-sort-key", 3)
|
|---|
| 4514 | + .add("visibility", "visible")
|
|---|
| 4515 | + .build();
|
|---|
| 4516 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4517 | + .add("line-blur", 5)
|
|---|
| 4518 | + .add("line-color", "#fff000") // color:#fff000;
|
|---|
| 4519 | + .add("line-dasharray", Json.createArrayBuilder().add(1).add(5).add(1)) // dashes:1,5,1;
|
|---|
| 4520 | + .add("line-gap-width", 6)
|
|---|
| 4521 | + .add("line-gradient", "#ffff00") // disabled by line-dasharray/line-pattern, source must be "geojson"
|
|---|
| 4522 | + .add("line-offset", 12)
|
|---|
| 4523 | + .add("line-opacity", 0.5) // opacity:0.5;
|
|---|
| 4524 | + .add("line-pattern", JsonValue.NULL)
|
|---|
| 4525 | + .add("line-translate", Json.createArrayBuilder().add(-1).add(-2))
|
|---|
| 4526 | + .add("line-translate-anchor", "viewport")
|
|---|
| 4527 | + .add("line-width", 22) // width:22;
|
|---|
| 4528 | + .build();
|
|---|
| 4529 | +
|
|---|
| 4530 | + // Test fully defined line
|
|---|
| 4531 | + Layers fullLineLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4532 | + .add("type", Layers.Type.LINE.name().toLowerCase(Locale.ROOT))
|
|---|
| 4533 | + .add("id", "random-layer-id")
|
|---|
| 4534 | + .add("source", "Random source")
|
|---|
| 4535 | + .add("layout", allLayoutProperties)
|
|---|
| 4536 | + .add("paint", allPaintProperties)
|
|---|
| 4537 | + .build());
|
|---|
| 4538 | + assertEquals("random-layer-id", fullLineLayer.getId());
|
|---|
| 4539 | + assertEquals(Layers.Type.LINE, fullLineLayer.getType());
|
|---|
| 4540 | + assertEquals("way::random-layer-id{color:#fff000;opacity:0.5;linecap:round;dashes:1,5,1;width:22;}", fullLineLayer.toString());
|
|---|
| 4541 | +
|
|---|
| 4542 | + // Test invisible line
|
|---|
| 4543 | + Layers fullLineInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4544 | + .add("type", Layers.Type.LINE.name().toLowerCase(Locale.ROOT))
|
|---|
| 4545 | + .add("id", "random-layer-id")
|
|---|
| 4546 | + .add("source", "Random source")
|
|---|
| 4547 | + .add("layout", Json.createObjectBuilder(allLayoutProperties)
|
|---|
| 4548 | + .add("visibility", "none"))
|
|---|
| 4549 | + .add("paint", allPaintProperties)
|
|---|
| 4550 | + .build());
|
|---|
| 4551 | + assertEquals("random-layer-id", fullLineInvisibleLayer.getId());
|
|---|
| 4552 | + assertEquals(Layers.Type.LINE, fullLineInvisibleLayer.getType());
|
|---|
| 4553 | + assertEquals("", fullLineInvisibleLayer.toString());
|
|---|
| 4554 | + }
|
|---|
| 4555 | +
|
|---|
| 4556 | + @Test
|
|---|
| 4557 | + void testSymbol() {
|
|---|
| 4558 | + // Test a layer without a source (should fail)
|
|---|
| 4559 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4560 | + .add("type", Layers.Type.SYMBOL.name())
|
|---|
| 4561 | + .add("id", "Empty Symbol").build()));
|
|---|
| 4562 | +
|
|---|
| 4563 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4564 | + .add("icon-color", "#fff000") // also requires sdf icons
|
|---|
| 4565 | + .add("icon-halo-blur", 5)
|
|---|
| 4566 | + .add("icon-halo-color", "#ffff00")
|
|---|
| 4567 | + .add("icon-halo-width", 6)
|
|---|
| 4568 | + .add("icon-opacity", 0.5) // icon-opacity:0.5;
|
|---|
| 4569 | + .add("icon-translate", Json.createArrayBuilder().add(11).add(12))
|
|---|
| 4570 | + .add("icon-translate-anchor", "viewport") // also requires icon-translate
|
|---|
| 4571 | + .add("text-color", "#fffff0") // text-color:#fffff0;
|
|---|
| 4572 | + .add("text-halo-blur", 15)
|
|---|
| 4573 | + .add("text-halo-color", "#ffffff") // text-halo-color:#ffffff;
|
|---|
| 4574 | + .add("text-halo-width", 16) // text-halo-radius:16;
|
|---|
| 4575 | + .add("text-opacity", 0.6) // text-opacity:0.6;
|
|---|
| 4576 | + .add("text-translate", Json.createArrayBuilder().add(26).add(27))
|
|---|
| 4577 | + .add("text-translate-anchor", "viewport")
|
|---|
| 4578 | + .build();
|
|---|
| 4579 | + JsonObject allLayoutProperties = Json.createObjectBuilder()
|
|---|
| 4580 | + .add("icon-allow-overlap", true)
|
|---|
| 4581 | + .add("icon-anchor", "left")
|
|---|
| 4582 | + .add("icon-ignore-placement", true)
|
|---|
| 4583 | + .add("icon-image", "random-image") // icon-image:concat(\"random-image\");
|
|---|
| 4584 | + .add("icon-keep-upright", true) // also requires icon-rotation-alignment=map and symbol-placement=line|line-center
|
|---|
| 4585 | + .add("icon-offset", Json.createArrayBuilder().add(2).add(3)) // icon-offset-x:2.0;icon-offset-y:3.0;
|
|---|
| 4586 | + .add("icon-optional", true) // also requires text-field
|
|---|
| 4587 | + .add("icon-padding", 4)
|
|---|
| 4588 | + .add("icon-pitch-alignment", "viewport")
|
|---|
| 4589 | + .add("icon-rotate", 30) // icon-rotation:30.0;
|
|---|
| 4590 | + .add("icon-rotation-alignment", "map")
|
|---|
| 4591 | + .add("icon-size", 2)
|
|---|
| 4592 | + .add("icon-text-fit", "width") // also requires text-field
|
|---|
| 4593 | + .add("icon-text-fit-padding", Json.createArrayBuilder().add(7).add(8).add(9).add(10))
|
|---|
| 4594 | + .add("symbol-avoid-edges", true)
|
|---|
| 4595 | + .add("symbol-placement", "line")
|
|---|
| 4596 | + .add("symbol-sort-key", 13)
|
|---|
| 4597 | + .add("symbol-spacing", 14) // requires symbol-placement=line
|
|---|
| 4598 | + .add("symbol-z-order", "source")
|
|---|
| 4599 | + .add("text-allow-overlap", true) // requires text-field
|
|---|
| 4600 | + .add("text-anchor", "left") // requires text-field, disabled by text-variable-anchor
|
|---|
| 4601 | + .add("text-field", "something") // text:something;
|
|---|
| 4602 | + .add("text-font", Json.createArrayBuilder().add("SansSerif")) // DroidSans isn't always available in an IDE
|
|---|
| 4603 | + .add("text-ignore-placement", true)
|
|---|
| 4604 | + .add("text-justify", "left")
|
|---|
| 4605 | + .add("text-keep-upright", false)
|
|---|
| 4606 | + .add("text-letter-spacing", 17)
|
|---|
| 4607 | + .add("text-line-height", 1.3)
|
|---|
| 4608 | + .add("text-max-angle", 18)
|
|---|
| 4609 | + .add("text-max-width", 19)
|
|---|
| 4610 | + .add("text-offset", Json.createArrayBuilder().add(20).add(21))
|
|---|
| 4611 | + .add("text-optional", true)
|
|---|
| 4612 | + .add("text-padding", 22)
|
|---|
| 4613 | + .add("text-pitch-alignment", "viewport")
|
|---|
| 4614 | + .add("text-radial-offset", 23)
|
|---|
| 4615 | + .add("text-rotate", 24)
|
|---|
| 4616 | + .add("text-rotation-alignment", "viewport")
|
|---|
| 4617 | + .add("text-size", 25) // font-size:25;
|
|---|
| 4618 | + .add("text-transform", "uppercase")
|
|---|
| 4619 | + .add("text-variable-anchor", "left")
|
|---|
| 4620 | + .add("text-writing-mode", "vertical")
|
|---|
| 4621 | + .add("visibility", "visible").build();
|
|---|
| 4622 | +
|
|---|
| 4623 | + // Test fully defined symbol
|
|---|
| 4624 | + Layers fullLineLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4625 | + .add("type", Layers.Type.SYMBOL.name().toLowerCase(Locale.ROOT))
|
|---|
| 4626 | + .add("id", "random-layer-id")
|
|---|
| 4627 | + .add("source", "Random source")
|
|---|
| 4628 | + .add("layout", allLayoutProperties)
|
|---|
| 4629 | + .add("paint", allPaintProperties)
|
|---|
| 4630 | + .build());
|
|---|
| 4631 | + assertEquals("random-layer-id", fullLineLayer.getId());
|
|---|
| 4632 | + assertEquals(Layers.Type.SYMBOL, fullLineLayer.getType());
|
|---|
| 4633 | + assertEquals("node::random-layer-id{icon-image:concat(\"random-image\");icon-offset-x:2.0;icon-offset-y:3.0;"
|
|---|
| 4634 | + + "icon-opacity:0.5;icon-rotation:30.0;text-color:#fffff0;text:something;font-family:\"SansSerif\";font-weight:normal;"
|
|---|
| 4635 | + + "font-style:normal;text-halo-color:#ffffff;text-halo-radius:16;text-opacity:0.6;font-size:25;}", fullLineLayer.toString());
|
|---|
| 4636 | +
|
|---|
| 4637 | + // Test an invisible symbol
|
|---|
| 4638 | + Layers fullLineInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4639 | + .add("type", Layers.Type.SYMBOL.name().toLowerCase(Locale.ROOT))
|
|---|
| 4640 | + .add("id", "random-layer-id")
|
|---|
| 4641 | + .add("source", "Random source")
|
|---|
| 4642 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("visibility", "none"))
|
|---|
| 4643 | + .add("paint", allPaintProperties)
|
|---|
| 4644 | + .build());
|
|---|
| 4645 | + assertEquals("random-layer-id", fullLineInvisibleLayer.getId());
|
|---|
| 4646 | + assertEquals(Layers.Type.SYMBOL, fullLineInvisibleLayer.getType());
|
|---|
| 4647 | + assertEquals("", fullLineInvisibleLayer.toString());
|
|---|
| 4648 | +
|
|---|
| 4649 | + // Test with placeholders in icon-image
|
|---|
| 4650 | + Layers fullOneIconImagePlaceholderLineLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4651 | + .add("type", Layers.Type.SYMBOL.name().toLowerCase(Locale.ROOT))
|
|---|
| 4652 | + .add("id", "random-layer-id")
|
|---|
| 4653 | + .add("source", "Random source")
|
|---|
| 4654 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("icon-image", "{value}"))
|
|---|
| 4655 | + .add("paint", allPaintProperties)
|
|---|
| 4656 | + .build());
|
|---|
| 4657 | + assertEquals("node::random-layer-id{icon-image:concat(tag(\"value\"));icon-offset-x:2.0;icon-offset-y:3.0;"
|
|---|
| 4658 | + + "icon-opacity:0.5;icon-rotation:30.0;text-color:#fffff0;text:something;font-family:\"SansSerif\";font-weight:normal;"
|
|---|
| 4659 | + + "font-style:normal;text-halo-color:#ffffff;text-halo-radius:16;text-opacity:0.6;font-size:25;}",
|
|---|
| 4660 | + fullOneIconImagePlaceholderLineLayer.toString());
|
|---|
| 4661 | +
|
|---|
| 4662 | + // Test with placeholders in icon-image
|
|---|
| 4663 | + Layers fullOneIconImagePlaceholderExtraLineLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4664 | + .add("type", Layers.Type.SYMBOL.name().toLowerCase(Locale.ROOT))
|
|---|
| 4665 | + .add("id", "random-layer-id")
|
|---|
| 4666 | + .add("source", "Random source")
|
|---|
| 4667 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("icon-image", "something/{value}/random"))
|
|---|
| 4668 | + .add("paint", allPaintProperties)
|
|---|
| 4669 | + .build());
|
|---|
| 4670 | + assertEquals("node::random-layer-id{icon-image:concat(\"something/\",tag(\"value\"),\"/random\");icon-offset-x:2.0;"
|
|---|
| 4671 | + + "icon-offset-y:3.0;icon-opacity:0.5;icon-rotation:30.0;text-color:#fffff0;text:something;font-family:\"SansSerif\";"
|
|---|
| 4672 | + + "font-weight:normal;font-style:normal;text-halo-color:#ffffff;text-halo-radius:16;text-opacity:0.6;font-size:25;}",
|
|---|
| 4673 | + fullOneIconImagePlaceholderExtraLineLayer.toString());
|
|---|
| 4674 | +
|
|---|
| 4675 | + // Test with placeholders in icon-image
|
|---|
| 4676 | + Layers fullTwoIconImagePlaceholderExtraLineLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4677 | + .add("type", Layers.Type.SYMBOL.name().toLowerCase(Locale.ROOT))
|
|---|
| 4678 | + .add("id", "random-layer-id")
|
|---|
| 4679 | + .add("source", "Random source")
|
|---|
| 4680 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("icon-image", "something/{value}/random/{value2}"))
|
|---|
| 4681 | + .add("paint", allPaintProperties)
|
|---|
| 4682 | + .build());
|
|---|
| 4683 | + assertEquals("node::random-layer-id{icon-image:concat(\"something/\",tag(\"value\"),\"/random/\",tag(\"value2\"));"
|
|---|
| 4684 | + + "icon-offset-x:2.0;icon-offset-y:3.0;icon-opacity:0.5;icon-rotation:30.0;text-color:#fffff0;text:something;"
|
|---|
| 4685 | + + "font-family:\"SansSerif\";font-weight:normal;font-style:normal;text-halo-color:#ffffff;text-halo-radius:16;"
|
|---|
| 4686 | + + "text-opacity:0.6;font-size:25;}", fullTwoIconImagePlaceholderExtraLineLayer.toString());
|
|---|
| 4687 | + }
|
|---|
| 4688 | +
|
|---|
| 4689 | + @Test
|
|---|
| 4690 | + void testRaster() {
|
|---|
| 4691 | + // Test a layer without a source (should fail)
|
|---|
| 4692 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4693 | + .add("type", Layers.Type.RASTER.name())
|
|---|
| 4694 | + .add("id", "Empty Raster").build()));
|
|---|
| 4695 | +
|
|---|
| 4696 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4697 | + .add("raster-brightness-max", 0.5)
|
|---|
| 4698 | + .add("raster-brightness-min", 0.6)
|
|---|
| 4699 | + .add("raster-contrast", 0.7)
|
|---|
| 4700 | + .add("raster-fade-duration", 1)
|
|---|
| 4701 | + .add("raster-hue-rotate", 2)
|
|---|
| 4702 | + .add("raster-opacity", 0.7)
|
|---|
| 4703 | + .add("raster-resampling", "nearest")
|
|---|
| 4704 | + .add("raster-saturation", 0.8)
|
|---|
| 4705 | + .build();
|
|---|
| 4706 | + JsonObject allLayoutProperties = Json.createObjectBuilder().add("visibility", "visible").build();
|
|---|
| 4707 | +
|
|---|
| 4708 | + // Test fully defined raster
|
|---|
| 4709 | + Layers fullRaster = new Layers(Json.createObjectBuilder()
|
|---|
| 4710 | + .add("id", "test-raster")
|
|---|
| 4711 | + .add("type", Layers.Type.RASTER.name().toLowerCase(Locale.ROOT))
|
|---|
| 4712 | + .add("source", "Random source")
|
|---|
| 4713 | + .add("layout", allLayoutProperties)
|
|---|
| 4714 | + .add("paint", allPaintProperties)
|
|---|
| 4715 | + .build());
|
|---|
| 4716 | + assertEquals(Layers.Type.RASTER, fullRaster.getType());
|
|---|
| 4717 | + assertEquals("test-raster", fullRaster.getId());
|
|---|
| 4718 | + assertEquals("Random source", fullRaster.getSource());
|
|---|
| 4719 | + assertEquals("", fullRaster.toString());
|
|---|
| 4720 | +
|
|---|
| 4721 | + // Test fully defined invisible raster
|
|---|
| 4722 | + Layers fullInvisibleRaster = new Layers(Json.createObjectBuilder()
|
|---|
| 4723 | + .add("id", "test-raster")
|
|---|
| 4724 | + .add("type", Layers.Type.RASTER.name().toLowerCase(Locale.ROOT))
|
|---|
| 4725 | + .add("source", "Random source")
|
|---|
| 4726 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("visibility", "none"))
|
|---|
| 4727 | + .add("paint", allPaintProperties)
|
|---|
| 4728 | + .build());
|
|---|
| 4729 | + assertEquals("", fullInvisibleRaster.toString());
|
|---|
| 4730 | + }
|
|---|
| 4731 | +
|
|---|
| 4732 | + @Test
|
|---|
| 4733 | + void testCircle() {
|
|---|
| 4734 | + // Test a layer without a source (should fail)
|
|---|
| 4735 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4736 | + .add("type", Layers.Type.CIRCLE.name())
|
|---|
| 4737 | + .add("id", "Empty Circle").build()));
|
|---|
| 4738 | +
|
|---|
| 4739 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4740 | + .add("circle-blur", 1)
|
|---|
| 4741 | + .add("circle-color", "#fff000") // symbol-fill-color:#fff000;
|
|---|
| 4742 | + .add("circle-opacity", 0.5) // symbol-fill-opacity:0.5;
|
|---|
| 4743 | + .add("circle-pitch-alignment", "map")
|
|---|
| 4744 | + .add("circle-pitch-scale", "viewport")
|
|---|
| 4745 | + .add("circle-radius", 2) // symbol-size:4.0; (we use width)
|
|---|
| 4746 | + .add("circle-stroke-color", "#ffff00") // symbol-stroke-color:#ffff00;
|
|---|
| 4747 | + .add("circle-stroke-opacity", 0.6) // symbol-stroke-opacity:0.6;
|
|---|
| 4748 | + .add("circle-stroke-width", 5) // symbol-stroke-width:5.0;
|
|---|
| 4749 | + .add("circle-translate", Json.createArrayBuilder().add(3).add(4))
|
|---|
| 4750 | + .add("circle-translate-anchor", "viewport")
|
|---|
| 4751 | + .build();
|
|---|
| 4752 | + JsonObject allLayoutProperties = Json.createObjectBuilder()
|
|---|
| 4753 | + .add("circle-sort-key", 3)
|
|---|
| 4754 | + .add("visibility", "visible")
|
|---|
| 4755 | + .build();
|
|---|
| 4756 | +
|
|---|
| 4757 | + Layers fullCircleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4758 | + .add("id", "Full circle layer")
|
|---|
| 4759 | + .add("type", Layers.Type.CIRCLE.name().toLowerCase(Locale.ROOT))
|
|---|
| 4760 | + .add("source", "Random source")
|
|---|
| 4761 | + .add("layout", allLayoutProperties)
|
|---|
| 4762 | + .add("paint", allPaintProperties)
|
|---|
| 4763 | + .build());
|
|---|
| 4764 | + assertEquals(Layers.Type.CIRCLE, fullCircleLayer.getType());
|
|---|
| 4765 | + assertEquals("Full circle layer", fullCircleLayer.getId());
|
|---|
| 4766 | + assertEquals("Random source", fullCircleLayer.getSource());
|
|---|
| 4767 | + assertEquals("node::Full circle layer{symbol-shape:circle;symbol-fill-color:#fff000;symbol-fill-opacity:0.5;"
|
|---|
| 4768 | + + "symbol-size:4.0;symbol-stroke-color:#ffff00;symbol-stroke-opacity:0.6;symbol-stroke-width:5;}", fullCircleLayer.toString());
|
|---|
| 4769 | +
|
|---|
| 4770 | + Layers fullCircleInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4771 | + .add("id", "Full circle layer")
|
|---|
| 4772 | + .add("type", Layers.Type.CIRCLE.name().toLowerCase(Locale.ROOT))
|
|---|
| 4773 | + .add("source", "Random source")
|
|---|
| 4774 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("visibility", "none"))
|
|---|
| 4775 | + .add("paint", allPaintProperties)
|
|---|
| 4776 | + .build());
|
|---|
| 4777 | + assertEquals(Layers.Type.CIRCLE, fullCircleInvisibleLayer.getType());
|
|---|
| 4778 | + assertEquals("Full circle layer", fullCircleInvisibleLayer.getId());
|
|---|
| 4779 | + assertEquals("Random source", fullCircleInvisibleLayer.getSource());
|
|---|
| 4780 | + assertEquals("", fullCircleInvisibleLayer.toString());
|
|---|
| 4781 | + }
|
|---|
| 4782 | +
|
|---|
| 4783 | + @Test
|
|---|
| 4784 | + void testFillExtrusion() {
|
|---|
| 4785 | + // Test a layer without a source (should fail)
|
|---|
| 4786 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4787 | + .add("type", Layers.Type.FILL_EXTRUSION.name())
|
|---|
| 4788 | + .add("id", "Empty Fill Extrusion").build()));
|
|---|
| 4789 | +
|
|---|
| 4790 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4791 | + .add("fill-extrusion-base", 1)
|
|---|
| 4792 | + .add("fill-extrusion-color", "#fff000")
|
|---|
| 4793 | + .add("fill-extrusion-height", 2)
|
|---|
| 4794 | + .add("fill-extrusion-opacity", 0.5)
|
|---|
| 4795 | + .add("fill-extrusion-pattern", "something-random")
|
|---|
| 4796 | + .add("fill-extrusion-translate", Json.createArrayBuilder().add(3).add(4))
|
|---|
| 4797 | + .add("fill-extrusion-translate-anchor", "viewport")
|
|---|
| 4798 | + .add("fill-extrusion-vertical-gradient", false)
|
|---|
| 4799 | + .build();
|
|---|
| 4800 | + JsonObject allLayoutProperties = Json.createObjectBuilder().add("visibility", "visible").build();
|
|---|
| 4801 | +
|
|---|
| 4802 | + Layers fullFillLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4803 | + .add("id", "Fill Extrusion")
|
|---|
| 4804 | + .add("type", Layers.Type.FILL_EXTRUSION.name().toLowerCase(Locale.ROOT))
|
|---|
| 4805 | + .add("source", "Random source")
|
|---|
| 4806 | + .add("layout", allLayoutProperties)
|
|---|
| 4807 | + .add("paint", allPaintProperties)
|
|---|
| 4808 | + .build());
|
|---|
| 4809 | + assertEquals("", fullFillLayer.toString());
|
|---|
| 4810 | + assertEquals(Layers.Type.FILL_EXTRUSION, fullFillLayer.getType());
|
|---|
| 4811 | + Layers fullFillInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4812 | + .add("id", "Fill Extrusion")
|
|---|
| 4813 | + .add("type", Layers.Type.FILL_EXTRUSION.name().toLowerCase(Locale.ROOT))
|
|---|
| 4814 | + .add("source", "Random source")
|
|---|
| 4815 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("visibility", "none"))
|
|---|
| 4816 | + .add("paint", allPaintProperties)
|
|---|
| 4817 | + .build());
|
|---|
| 4818 | + assertEquals("", fullFillInvisibleLayer.toString());
|
|---|
| 4819 | + assertEquals(Layers.Type.FILL_EXTRUSION, fullFillInvisibleLayer.getType());
|
|---|
| 4820 | + }
|
|---|
| 4821 | +
|
|---|
| 4822 | + @Test
|
|---|
| 4823 | + void testHeatmap() {
|
|---|
| 4824 | + // Test a layer without a source (should fail)
|
|---|
| 4825 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4826 | + .add("type", Layers.Type.HEATMAP.name())
|
|---|
| 4827 | + .add("id", "Empty Heatmap").build()));
|
|---|
| 4828 | +
|
|---|
| 4829 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4830 | + .add("heatmap-color", "#fff000") // This will probably be a gradient of some type
|
|---|
| 4831 | + .add("heatmap-intensity", 0.5)
|
|---|
| 4832 | + .add("heatmap-opacity", 0.6)
|
|---|
| 4833 | + .add("heatmap-radius", 1) // This is in pixels
|
|---|
| 4834 | + .add("heatmap-weight", 0.7)
|
|---|
| 4835 | + .build();
|
|---|
| 4836 | + JsonObject allLayoutProperties = Json.createObjectBuilder().add("visibility", "visible").build();
|
|---|
| 4837 | +
|
|---|
| 4838 | + Layers fullHeatmapLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4839 | + .add("id", "Full heatmap")
|
|---|
| 4840 | + .add("type", Layers.Type.HEATMAP.name().toLowerCase(Locale.ROOT))
|
|---|
| 4841 | + .add("source", "Random source")
|
|---|
| 4842 | + .add("paint", allPaintProperties)
|
|---|
| 4843 | + .add("layout", allLayoutProperties)
|
|---|
| 4844 | + .build());
|
|---|
| 4845 | + assertEquals(Layers.Type.HEATMAP, fullHeatmapLayer.getType());
|
|---|
| 4846 | + assertEquals("", fullHeatmapLayer.toString());
|
|---|
| 4847 | +
|
|---|
| 4848 | + Layers fullHeatmapInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4849 | + .add("id", "Full heatmap")
|
|---|
| 4850 | + .add("type", Layers.Type.HEATMAP.name().toLowerCase(Locale.ROOT))
|
|---|
| 4851 | + .add("source", "Random source")
|
|---|
| 4852 | + .add("paint", allPaintProperties)
|
|---|
| 4853 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("visibility", "none"))
|
|---|
| 4854 | + .build());
|
|---|
| 4855 | + assertEquals(Layers.Type.HEATMAP, fullHeatmapInvisibleLayer.getType());
|
|---|
| 4856 | + assertEquals("", fullHeatmapInvisibleLayer.toString());
|
|---|
| 4857 | + }
|
|---|
| 4858 | +
|
|---|
| 4859 | + @Test
|
|---|
| 4860 | + void testHillshade() {
|
|---|
| 4861 | + // Test a layer without a source (should fail)
|
|---|
| 4862 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4863 | + .add("type", Layers.Type.HILLSHADE.name())
|
|---|
| 4864 | + .add("id", "Empty Hillshade").build()));
|
|---|
| 4865 | +
|
|---|
| 4866 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4867 | + .add("hillshade-accent-color", "#fff000")
|
|---|
| 4868 | + .add("hillshade-exaggeration", 0.6)
|
|---|
| 4869 | + .add("hillshade-highlight-color", "#ffff00")
|
|---|
| 4870 | + .add("hillshade-illumination-anchor", "map")
|
|---|
| 4871 | + .add("hillshade-illumination-direction", 90)
|
|---|
| 4872 | + .add("hillshade-shadow-color", "#fffff0")
|
|---|
| 4873 | + .build();
|
|---|
| 4874 | + JsonObject allLayoutProperties = Json.createObjectBuilder()
|
|---|
| 4875 | + .add("visibility", "visible")
|
|---|
| 4876 | + .build();
|
|---|
| 4877 | +
|
|---|
| 4878 | + Layers fullHillshadeLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4879 | + .add("id", "Hillshade")
|
|---|
| 4880 | + .add("type", Layers.Type.HILLSHADE.toString().toLowerCase(Locale.ROOT))
|
|---|
| 4881 | + .add("source", "Random source")
|
|---|
| 4882 | + .add("paint", allPaintProperties)
|
|---|
| 4883 | + .add("layout", allLayoutProperties)
|
|---|
| 4884 | + .build());
|
|---|
| 4885 | + assertEquals(Layers.Type.HILLSHADE, fullHillshadeLayer.getType());
|
|---|
| 4886 | + assertEquals("", fullHillshadeLayer.toString());
|
|---|
| 4887 | +
|
|---|
| 4888 | + Layers fullHillshadeInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4889 | + .add("id", "Hillshade")
|
|---|
| 4890 | + .add("type", Layers.Type.HILLSHADE.toString().toLowerCase(Locale.ROOT))
|
|---|
| 4891 | + .add("source", "Random source")
|
|---|
| 4892 | + .add("paint", allPaintProperties)
|
|---|
| 4893 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("visibility", "none"))
|
|---|
| 4894 | + .build());
|
|---|
| 4895 | + assertEquals(Layers.Type.HILLSHADE, fullHillshadeInvisibleLayer.getType());
|
|---|
| 4896 | + assertEquals("", fullHillshadeInvisibleLayer.toString());
|
|---|
| 4897 | + }
|
|---|
| 4898 | +
|
|---|
| 4899 | + @Test
|
|---|
| 4900 | + void testSky() {
|
|---|
| 4901 | + // Test a layer without a source (should fail)
|
|---|
| 4902 | + assertThrows(NullPointerException.class, () -> new Layers(Json.createObjectBuilder()
|
|---|
| 4903 | + .add("type", Layers.Type.SKY.name())
|
|---|
| 4904 | + .add("id", "Empty Sky").build()));
|
|---|
| 4905 | +
|
|---|
| 4906 | + JsonObject allPaintProperties = Json.createObjectBuilder()
|
|---|
| 4907 | + .add("sky-atmosphere-color", "red")
|
|---|
| 4908 | + .add("sky-atmosphere-halo-color", "yellow")
|
|---|
| 4909 | + // 360180 is apparently included in this? Or it might be a formatting issue in the docs.
|
|---|
| 4910 | + .add("sky-atmosphere-sun", Json.createArrayBuilder().add(0, 360180))
|
|---|
| 4911 | + .add("sky-atmosphere-sun-intensity", 99)
|
|---|
| 4912 | + .add("sky-gradient", "#fff000")
|
|---|
| 4913 | + .add("sky-gradient-center", Json.createArrayBuilder().add(0).add(360180)) // see note on 360180 above
|
|---|
| 4914 | + .add("sky-gradient-radius", 1)
|
|---|
| 4915 | + .add("sky-opacity", 0.5)
|
|---|
| 4916 | + .add("sky-type", "gradient")
|
|---|
| 4917 | + .build();
|
|---|
| 4918 | + JsonObject allLayoutProperties = Json.createObjectBuilder().add("visibility", "visible").build();
|
|---|
| 4919 | +
|
|---|
| 4920 | + Layers fullSkyLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4921 | + .add("id", "Sky")
|
|---|
| 4922 | + .add("type", Layers.Type.SKY.name().toLowerCase(Locale.ROOT))
|
|---|
| 4923 | + .add("source", "Random source")
|
|---|
| 4924 | + .add("paint", allPaintProperties)
|
|---|
| 4925 | + .add("layout", allLayoutProperties)
|
|---|
| 4926 | + .build());
|
|---|
| 4927 | + assertEquals(Layers.Type.SKY, fullSkyLayer.getType());
|
|---|
| 4928 | + assertEquals("", fullSkyLayer.toString());
|
|---|
| 4929 | +
|
|---|
| 4930 | + Layers fullSkyInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 4931 | + .add("id", "Sky")
|
|---|
| 4932 | + .add("type", Layers.Type.SKY.name().toLowerCase(Locale.ROOT))
|
|---|
| 4933 | + .add("source", "Random source")
|
|---|
| 4934 | + .add("paint", allPaintProperties)
|
|---|
| 4935 | + .add("layout", Json.createObjectBuilder(allLayoutProperties).add("visibility", "none"))
|
|---|
| 4936 | + .build());
|
|---|
| 4937 | + assertEquals(Layers.Type.SKY, fullSkyInvisibleLayer.getType());
|
|---|
| 4938 | + assertEquals("", fullSkyInvisibleLayer.toString());
|
|---|
| 4939 | + }
|
|---|
| 4940 | +
|
|---|
| 4941 | + @Test
|
|---|
| 4942 | + void testZoomLevels() {
|
|---|
| 4943 | + JsonObject baseInformation = Json.createObjectBuilder()
|
|---|
| 4944 | + .add("id", "dots")
|
|---|
| 4945 | + .add("type", "CiRcLe")
|
|---|
| 4946 | + .add("source", "osm-source")
|
|---|
| 4947 | + .add("source-layer", "osm-images")
|
|---|
| 4948 | + .add("paint", Json.createObjectBuilder()
|
|---|
| 4949 | + .add("circle-color", "#fff000")
|
|---|
| 4950 | + .add("circle-radius", 6)
|
|---|
| 4951 | + ).build();
|
|---|
| 4952 | + Layers noZoomLayer = new Layers(baseInformation);
|
|---|
| 4953 | + String baseString = "node{0}::dots'{symbol-shape:circle;symbol-fill-color:#fff000;symbol-fill-opacity:1;"
|
|---|
| 4954 | + + "symbol-size:12.0;symbol-stroke-color:#000000;symbol-stroke-opacity:1;symbol-stroke-width:0;}'";
|
|---|
| 4955 | + assertEquals("osm-images", noZoomLayer.getSourceLayer());
|
|---|
| 4956 | + assertEquals(MessageFormat.format(baseString, ""), noZoomLayer.toString());
|
|---|
| 4957 | +
|
|---|
| 4958 | + Layers minZoomLayer = new Layers(Json.createObjectBuilder(baseInformation)
|
|---|
| 4959 | + .add("minzoom", 0)
|
|---|
| 4960 | + .build());
|
|---|
| 4961 | + assertEquals(MessageFormat.format(baseString, "|z0-"), minZoomLayer.toString());
|
|---|
| 4962 | +
|
|---|
| 4963 | + Layers maxZoomLayer = new Layers(Json.createObjectBuilder(baseInformation)
|
|---|
| 4964 | + .add("maxzoom", 24)
|
|---|
| 4965 | + .build());
|
|---|
| 4966 | + assertEquals(MessageFormat.format(baseString, "|z-24"), maxZoomLayer.toString());
|
|---|
| 4967 | +
|
|---|
| 4968 | + Layers minMaxZoomLayer = new Layers(Json.createObjectBuilder(baseInformation)
|
|---|
| 4969 | + .add("minzoom", 1)
|
|---|
| 4970 | + .add("maxzoom", 2)
|
|---|
| 4971 | + .build());
|
|---|
| 4972 | + assertEquals(MessageFormat.format(baseString, "|z1-2"), minMaxZoomLayer.toString());
|
|---|
| 4973 | +
|
|---|
| 4974 | + Layers sameMinMaxZoomLayer = new Layers(Json.createObjectBuilder(baseInformation)
|
|---|
| 4975 | + .add("minzoom", 2)
|
|---|
| 4976 | + .add("maxzoom", 2)
|
|---|
| 4977 | + .build());
|
|---|
| 4978 | + assertEquals(MessageFormat.format(baseString, "|z2"), sameMinMaxZoomLayer.toString());
|
|---|
| 4979 | + }
|
|---|
| 4980 | +
|
|---|
| 4981 | + @Test
|
|---|
| 4982 | + void testEquals() {
|
|---|
| 4983 | + EqualsVerifier.forClass(Layers.class).usingGetClass().verify();
|
|---|
| 4984 | + }
|
|---|
| 4985 | +}
|
|---|
| 4986 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyleTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyleTest.java
|
|---|
| 4987 | new file mode 100644
|
|---|
| 4988 | index 000000000..1fcb7bfe8
|
|---|
| 4989 | --- /dev/null
|
|---|
| 4990 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyleTest.java
|
|---|
| 4991 | @@ -0,0 +1,300 @@
|
|---|
| 4992 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 4993 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 4994 | +
|
|---|
| 4995 | +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|---|
| 4996 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 4997 | +import static org.junit.jupiter.api.Assertions.assertFalse;
|
|---|
| 4998 | +import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|---|
| 4999 | +import static org.junit.jupiter.api.Assertions.assertThrows;
|
|---|
| 5000 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 5001 | +import static org.junit.jupiter.api.Assertions.fail;
|
|---|
| 5002 | +
|
|---|
| 5003 | +
|
|---|
| 5004 | +import java.awt.Color;
|
|---|
| 5005 | +import java.awt.Graphics2D;
|
|---|
| 5006 | +import java.awt.image.BufferedImage;
|
|---|
| 5007 | +import java.io.ByteArrayInputStream;
|
|---|
| 5008 | +import java.io.File;
|
|---|
| 5009 | +import java.io.FileOutputStream;
|
|---|
| 5010 | +import java.io.IOException;
|
|---|
| 5011 | +import java.nio.charset.StandardCharsets;
|
|---|
| 5012 | +import java.nio.file.Paths;
|
|---|
| 5013 | +import java.text.MessageFormat;
|
|---|
| 5014 | +import java.util.ArrayList;
|
|---|
| 5015 | +import java.util.Collection;
|
|---|
| 5016 | +import java.util.Map;
|
|---|
| 5017 | +import java.util.Objects;
|
|---|
| 5018 | +import java.util.Optional;
|
|---|
| 5019 | +import java.util.concurrent.atomic.AtomicBoolean;
|
|---|
| 5020 | +import java.util.stream.Collectors;
|
|---|
| 5021 | +
|
|---|
| 5022 | +import javax.imageio.ImageIO;
|
|---|
| 5023 | +import javax.json.Json;
|
|---|
| 5024 | +import javax.json.JsonObject;
|
|---|
| 5025 | +import javax.json.JsonObjectBuilder;
|
|---|
| 5026 | +import javax.json.JsonReader;
|
|---|
| 5027 | +import javax.json.JsonStructure;
|
|---|
| 5028 | +import javax.json.JsonValue;
|
|---|
| 5029 | +
|
|---|
| 5030 | +import org.openstreetmap.josm.TestUtils;
|
|---|
| 5031 | +import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 5032 | +import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 5033 | +import org.openstreetmap.josm.gui.mappaint.Keyword;
|
|---|
| 5034 | +import org.openstreetmap.josm.gui.mappaint.StyleSource;
|
|---|
| 5035 | +import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
|
|---|
| 5036 | +import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
|
|---|
| 5037 | +import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
|
|---|
| 5038 | +import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 5039 | +import org.openstreetmap.josm.tools.ColorHelper;
|
|---|
| 5040 | +import org.openstreetmap.josm.tools.ImageProvider;
|
|---|
| 5041 | +
|
|---|
| 5042 | +import nl.jqno.equalsverifier.EqualsVerifier;
|
|---|
| 5043 | +import org.awaitility.Awaitility;
|
|---|
| 5044 | +import org.awaitility.Durations;
|
|---|
| 5045 | +import org.junit.jupiter.api.Test;
|
|---|
| 5046 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 5047 | +import org.junit.jupiter.api.io.TempDir;
|
|---|
| 5048 | +
|
|---|
| 5049 | +/**
|
|---|
| 5050 | + * Test class for {@link MapBoxVectorStyle}
|
|---|
| 5051 | + * @author Taylor Smock
|
|---|
| 5052 | + */
|
|---|
| 5053 | +public class MapBoxVectorStyleTest {
|
|---|
| 5054 | + /** Used to store sprite files (specifically, sprite{,@2x}.{png,json}) */
|
|---|
| 5055 | + @TempDir
|
|---|
| 5056 | + File spritesDirectory;
|
|---|
| 5057 | +
|
|---|
| 5058 | + // Needed for osm primitives (we really just need to initialize the config)
|
|---|
| 5059 | + // OSM primitives are called when we load style sources
|
|---|
| 5060 | + @RegisterExtension
|
|---|
| 5061 | + JOSMTestRules rules = new JOSMTestRules();
|
|---|
| 5062 | +
|
|---|
| 5063 | + /** The base information */
|
|---|
| 5064 | + private static final String BASE_STYLE = "'{'\"version\":8,\"name\":\"test style\",\"owner\":\"josm test\",\"id\":\"{0}\",{1}'}'";
|
|---|
| 5065 | + /** Source 1 */
|
|---|
| 5066 | + private static final String SOURCE1 = "\"source1\":{\"type\":\"vector\",\"tiles\":[\"https://example.org/{z}/{x}/{y}.mvt\"]}";
|
|---|
| 5067 | + /** Layer 1 */
|
|---|
| 5068 | + private static final String LAYER1 = "{\"id\":\"layer1\",\"type\":\"circle\",\"source\":\"source1\",\"source-layer\":\"nodes\"}";
|
|---|
| 5069 | + /** Source 2 */
|
|---|
| 5070 | + private static final String SOURCE2 = "\"source2\":{\"type\":\"vector\",\"tiles\":[\"https://example.org/{z}2/{x}/{y}.mvt\"]}";
|
|---|
| 5071 | + /** Layer 2 */
|
|---|
| 5072 | + private static final String LAYER2 = "{\"id\":\"layer2\",\"type\":\"circle\",\"source\":\"source2\",\"source-layer\":\"nodes\"}";
|
|---|
| 5073 | +
|
|---|
| 5074 | + /**
|
|---|
| 5075 | + * Check that the version matches the supported style version(s). Currently, only version 8 exists and is (partially)
|
|---|
| 5076 | + * supported.
|
|---|
| 5077 | + */
|
|---|
| 5078 | + @Test
|
|---|
| 5079 | + void testVersionChecks() {
|
|---|
| 5080 | + assertThrows(NullPointerException.class, () -> new MapBoxVectorStyle(JsonValue.EMPTY_JSON_OBJECT));
|
|---|
| 5081 | + IllegalArgumentException badVersion = assertThrows(IllegalArgumentException.class,
|
|---|
| 5082 | + () -> new MapBoxVectorStyle(Json.createObjectBuilder().add("version", 7).build()));
|
|---|
| 5083 | + assertEquals("Vector Tile Style Version not understood: version 7 (json: {\"version\":7})", badVersion.getMessage());
|
|---|
| 5084 | + badVersion = assertThrows(IllegalArgumentException.class,
|
|---|
| 5085 | + () -> new MapBoxVectorStyle(Json.createObjectBuilder().add("version", 9).build()));
|
|---|
| 5086 | + assertEquals("Vector Tile Style Version not understood: version 9 (json: {\"version\":9})", badVersion.getMessage());
|
|---|
| 5087 | + assertDoesNotThrow(() -> new MapBoxVectorStyle(Json.createObjectBuilder().add("version", 8).build()));
|
|---|
| 5088 | + }
|
|---|
| 5089 | +
|
|---|
| 5090 | + @Test
|
|---|
| 5091 | + void testSources() {
|
|---|
| 5092 | + // Check with an invalid sources list
|
|---|
| 5093 | + assertTrue(new MapBoxVectorStyle(getJson(JsonObject.class, "{\"version\":8,\"sources\":[\"s1\",\"s2\"]}")).getSources().isEmpty());
|
|---|
| 5094 | + Map<Source, ElemStyles> sources = new MapBoxVectorStyle(getJson(JsonObject.class, MessageFormat.format(BASE_STYLE, "test",
|
|---|
| 5095 | + MessageFormat.format("\"sources\":'{'{0},{1},\"source3\":[\"bad source\"]'}',\"layers\":[{2},{3},{4}]",
|
|---|
| 5096 | + SOURCE1, SOURCE2, LAYER1, LAYER2, LAYER2.replace('2', '3'))))).getSources();
|
|---|
| 5097 | + assertEquals(3, sources.size());
|
|---|
| 5098 | + assertTrue(sources.containsKey(null)); // This is due to there being no source3 layer
|
|---|
| 5099 | + sources.remove(null); // Avoid null checks later
|
|---|
| 5100 | + assertTrue(sources.keySet().stream().map(Source::getName).anyMatch("source1"::equals));
|
|---|
| 5101 | + assertTrue(sources.keySet().stream().map(Source::getName).anyMatch("source2"::equals));
|
|---|
| 5102 | + assertTrue(sources.keySet().stream().map(Source::getName).noneMatch("source3"::equals));
|
|---|
| 5103 | + }
|
|---|
| 5104 | +
|
|---|
| 5105 | + @Test
|
|---|
| 5106 | + void testSavedFiles() {
|
|---|
| 5107 | + assertTrue(new MapBoxVectorStyle(getJson(JsonObject.class, "{\"version\":8,\"sources\":[\"s1\",\"s2\"]}")).getSources().isEmpty());
|
|---|
| 5108 | + Map<Source, ElemStyles> sources = new MapBoxVectorStyle(getJson(JsonObject.class, MessageFormat.format(BASE_STYLE, "test",
|
|---|
| 5109 | + MessageFormat.format("\"sources\":'{'{0},{1}'}',\"layers\":[{2},{3}]", SOURCE1, SOURCE2, LAYER1, LAYER2)))).getSources();
|
|---|
| 5110 | + assertEquals(2, sources.size());
|
|---|
| 5111 | + // For various reasons, the map _must_ be reliably ordered in the order of encounter
|
|---|
| 5112 | + Source source1 = sources.keySet().iterator().next();
|
|---|
| 5113 | + Source source2 = sources.keySet().stream().skip(1).findFirst().orElseGet(() -> fail("No second source"));
|
|---|
| 5114 | + assertEquals("source1", source1.getName());
|
|---|
| 5115 | + assertEquals("source2", source2.getName());
|
|---|
| 5116 | +
|
|---|
| 5117 | + // Check that the files have been saved. Ideally, we would check that they haven't been
|
|---|
| 5118 | + // saved earlier, since this is in a different thread. Unfortunately, that is a _race condition_.
|
|---|
| 5119 | + MapCSSStyleSource styleSource1 = (MapCSSStyleSource) sources.get(source1).getStyleSources().get(0);
|
|---|
| 5120 | + MapCSSStyleSource styleSource2 = (MapCSSStyleSource) sources.get(source2).getStyleSources().get(0);
|
|---|
| 5121 | +
|
|---|
| 5122 | + AtomicBoolean saveFinished = new AtomicBoolean();
|
|---|
| 5123 | + MainApplication.worker.execute(() -> saveFinished.set(true));
|
|---|
| 5124 | + Awaitility.await().atMost(Durations.ONE_SECOND).until(saveFinished::get);
|
|---|
| 5125 | +
|
|---|
| 5126 | + assertTrue(styleSource1.url.endsWith("source1.mapcss"));
|
|---|
| 5127 | + assertTrue(styleSource2.url.endsWith("source2.mapcss"));
|
|---|
| 5128 | +
|
|---|
| 5129 | + MapCSSStyleSource mapCSSStyleSource1 = new MapCSSStyleSource(styleSource1.url, styleSource1.name, styleSource1.title);
|
|---|
| 5130 | + MapCSSStyleSource mapCSSStyleSource2 = new MapCSSStyleSource(styleSource2.url, styleSource2.name, styleSource2.title);
|
|---|
| 5131 | +
|
|---|
| 5132 | + assertEquals(styleSource1, mapCSSStyleSource1);
|
|---|
| 5133 | + assertEquals(styleSource2, mapCSSStyleSource2);
|
|---|
| 5134 | + }
|
|---|
| 5135 | +
|
|---|
| 5136 | + @Test
|
|---|
| 5137 | + void testSprites() throws IOException {
|
|---|
| 5138 | + generateSprites(false);
|
|---|
| 5139 | + // Ensure that we fall back to 1x sprites
|
|---|
| 5140 | + assertTrue(new File(this.spritesDirectory, "sprite.png").exists());
|
|---|
| 5141 | + assertFalse(new File(this.spritesDirectory, "sprite@2x.png").exists());
|
|---|
| 5142 | + assertTrue(new File(this.spritesDirectory, "sprite.json").exists());
|
|---|
| 5143 | + assertFalse(new File(this.spritesDirectory, "sprite@2x.json").exists());
|
|---|
| 5144 | +
|
|---|
| 5145 | + checkImages(false);
|
|---|
| 5146 | +
|
|---|
| 5147 | + generateSprites(true);
|
|---|
| 5148 | + checkImages(true);
|
|---|
| 5149 | + }
|
|---|
| 5150 | +
|
|---|
| 5151 | + private void checkImages(boolean hiDpi) {
|
|---|
| 5152 | + // Ensure that we don't have images saved in the ImageProvider cache
|
|---|
| 5153 | + ImageProvider.clearCache();
|
|---|
| 5154 | + int hiDpiScalar = hiDpi ? 2 : 1;
|
|---|
| 5155 | + String spritePath = new File(this.spritesDirectory, "sprite").getPath();
|
|---|
| 5156 | + MapBoxVectorStyle style = new MapBoxVectorStyle(getJson(JsonObject.class,
|
|---|
| 5157 | + MessageFormat.format(BASE_STYLE, "sprite_test", "\"sprite\":\"file:/" + spritePath + "\"")));
|
|---|
| 5158 | + assertEquals("file:/" + spritePath, style.getSpriteUrl());
|
|---|
| 5159 | +
|
|---|
| 5160 | + AtomicBoolean saveFinished = new AtomicBoolean();
|
|---|
| 5161 | + MainApplication.worker.execute(() -> saveFinished.set(true));
|
|---|
| 5162 | + Awaitility.await().atMost(Durations.ONE_SECOND).until(saveFinished::get);
|
|---|
| 5163 | +
|
|---|
| 5164 | + int scalar = 28; // 255 / 9 (could be 4, but this was a nicer number)
|
|---|
| 5165 | + for (int x = 0; x < 3; x++) {
|
|---|
| 5166 | + for (int y = 0; y < 3; y++) {
|
|---|
| 5167 | + // Expected color
|
|---|
| 5168 | + Color color = new Color(scalar * x, scalar * y, scalar * x * y);
|
|---|
| 5169 | + int finalX = x;
|
|---|
| 5170 | + int finalY = y;
|
|---|
| 5171 | + BufferedImage image = (BufferedImage) assertDoesNotThrow(
|
|---|
| 5172 | + () -> ImageProvider.get(new File("test style", MessageFormat.format("({0},{1})", finalX, finalY)).getPath()))
|
|---|
| 5173 | + .getImage();
|
|---|
| 5174 | + assertEquals(3 * hiDpiScalar, image.getWidth(null));
|
|---|
| 5175 | + assertEquals(3 * hiDpiScalar, image.getHeight(null));
|
|---|
| 5176 | + for (int x2 = 0; x2 < image.getWidth(null); x2++) {
|
|---|
| 5177 | + for (int y2 = 0; y2 < image.getHeight(null); y2++) {
|
|---|
| 5178 | + assertEquals(color.getRGB(), image.getRGB(x2, y2));
|
|---|
| 5179 | + }
|
|---|
| 5180 | + }
|
|---|
| 5181 | + }
|
|---|
| 5182 | + }
|
|---|
| 5183 | + }
|
|---|
| 5184 | +
|
|---|
| 5185 | + private void generateSprites(boolean hiDpi) throws IOException {
|
|---|
| 5186 | + // Create a 3x3 grid of 3x3 or 6x6 pixel squares (depends upon the dpi setting)
|
|---|
| 5187 | + int hiDpiScale = hiDpi ? 2 : 1;
|
|---|
| 5188 | + BufferedImage nineByNine = new BufferedImage(hiDpiScale * 9, hiDpiScale * 9, BufferedImage.TYPE_4BYTE_ABGR);
|
|---|
| 5189 | + int scalar = 28; // 255 / 9 (could be 4, but this was a nicer number)
|
|---|
| 5190 | + Graphics2D g = nineByNine.createGraphics();
|
|---|
| 5191 | + JsonObjectBuilder json = Json.createObjectBuilder();
|
|---|
| 5192 | + for (int x = 0; x < 3; x++) {
|
|---|
| 5193 | + for (int y = 0; y < 3; y++) {
|
|---|
| 5194 | + Color color = new Color(scalar * x, scalar * y, scalar * x * y);
|
|---|
| 5195 | + g.setColor(color);
|
|---|
| 5196 | + g.drawRect(3 * hiDpiScale * x, 3 * hiDpiScale * y, 3 * hiDpiScale, 3 * hiDpiScale);
|
|---|
| 5197 | + g.fillRect(3 * hiDpiScale * x, 3 * hiDpiScale * y, 3 * hiDpiScale, 3 * hiDpiScale);
|
|---|
| 5198 | +
|
|---|
| 5199 | + JsonObjectBuilder sprite = Json.createObjectBuilder();
|
|---|
| 5200 | + sprite.add("height", hiDpiScale * 3);
|
|---|
| 5201 | + sprite.add("pixelRatio", hiDpiScale);
|
|---|
| 5202 | + sprite.add("width", hiDpiScale * 3);
|
|---|
| 5203 | + sprite.add("x", 3 * hiDpiScale * x);
|
|---|
| 5204 | + sprite.add("y", 3 * hiDpiScale * y);
|
|---|
| 5205 | +
|
|---|
| 5206 | + json.add(MessageFormat.format("({0},{1})", x, y), sprite);
|
|---|
| 5207 | + }
|
|---|
| 5208 | + }
|
|---|
| 5209 | + String imageName = hiDpi ? "sprite@2x.png" : "sprite.png";
|
|---|
| 5210 | + ImageIO.write(nineByNine, "png", new File(this.spritesDirectory, imageName));
|
|---|
| 5211 | + String jsonName = hiDpi ? "sprite@2x.json" : "sprite.json";
|
|---|
| 5212 | + File jsonFile = new File(this.spritesDirectory, jsonName);
|
|---|
| 5213 | + try (FileOutputStream fileOutputStream = new FileOutputStream(jsonFile)) {
|
|---|
| 5214 | + fileOutputStream.write(json.build().toString().getBytes(StandardCharsets.UTF_8));
|
|---|
| 5215 | + }
|
|---|
| 5216 | + }
|
|---|
| 5217 | +
|
|---|
| 5218 | + private static <T extends JsonStructure> T getJson(Class<T> clazz, String json) {
|
|---|
| 5219 | + try (JsonReader reader = Json.createReader(new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)))) {
|
|---|
| 5220 | + JsonStructure structure = reader.read();
|
|---|
| 5221 | + if (clazz.isAssignableFrom(structure.getClass())) {
|
|---|
| 5222 | + return clazz.cast(structure);
|
|---|
| 5223 | + }
|
|---|
| 5224 | + }
|
|---|
| 5225 | + fail("Could not cast to expected class");
|
|---|
| 5226 | + throw new IllegalArgumentException();
|
|---|
| 5227 | + }
|
|---|
| 5228 | +
|
|---|
| 5229 | + @Test
|
|---|
| 5230 | + void testMapillaryStyle() {
|
|---|
| 5231 | + final String file = Paths.get("file:", TestUtils.getTestDataRoot(), "mapillary.json").toString();
|
|---|
| 5232 | + final MapBoxVectorStyle style = MapBoxVectorStyle.getMapBoxVectorStyle(file);
|
|---|
| 5233 | + assertNotNull(style);
|
|---|
| 5234 | + // There are three "sources" in the mapillary.json file
|
|---|
| 5235 | + assertEquals(3, style.getSources().size());
|
|---|
| 5236 | + final ElemStyles mapillarySource = style.getSources().entrySet().stream()
|
|---|
| 5237 | + .filter(source -> "mapillary-source".equals(source.getKey().getName())).map(
|
|---|
| 5238 | + Map.Entry::getValue).findAny().orElse(null);
|
|---|
| 5239 | + assertNotNull(mapillarySource);
|
|---|
| 5240 | + mapillarySource.getStyleSources().forEach(StyleSource::loadStyleSource);
|
|---|
| 5241 | + assertEquals(1, mapillarySource.getStyleSources().size());
|
|---|
| 5242 | + final MapCSSStyleSource mapillaryCssSource = (MapCSSStyleSource) mapillarySource.getStyleSources().get(0);
|
|---|
| 5243 | + assertTrue(mapillaryCssSource.getErrors().isEmpty());
|
|---|
| 5244 | + final MapCSSRule mapillaryOverview = getRule(mapillaryCssSource, "node", "mapillary-overview");
|
|---|
| 5245 | + assertNotNull(mapillaryOverview);
|
|---|
| 5246 | + assertInInstructions(mapillaryOverview.declaration.instructions, "symbol-shape", new Keyword("circle"));
|
|---|
| 5247 | + assertInInstructions(mapillaryOverview.declaration.instructions, "symbol-fill-color", ColorHelper.html2color("#05CB63"));
|
|---|
| 5248 | + assertInInstructions(mapillaryOverview.declaration.instructions, "symbol-fill-opacity", 0.6f);
|
|---|
| 5249 | + // Docs indicate that symbol-size is total width, while we are translating from a radius. So 2 * 4 = 8.
|
|---|
| 5250 | + assertInInstructions(mapillaryOverview.declaration.instructions, "symbol-size", 8.0f);
|
|---|
| 5251 | + }
|
|---|
| 5252 | +
|
|---|
| 5253 | + @Test
|
|---|
| 5254 | + void testEqualsContract() {
|
|---|
| 5255 | + // We need to "load" the style sources to avoid the verifier from thinking they are equal
|
|---|
| 5256 | + StyleSource canvas = new MapCSSStyleSource("meta{title:\"canvas\";}canvas{default-points:false;}");
|
|---|
| 5257 | + StyleSource node = new MapCSSStyleSource("meta{title:\"node\";}node{text:ref;}");
|
|---|
| 5258 | + node.loadStyleSource();
|
|---|
| 5259 | + canvas.loadStyleSource();
|
|---|
| 5260 | + EqualsVerifier.forClass(MapBoxVectorStyle.class)
|
|---|
| 5261 | + .withPrefabValues(ImageProvider.class, new ImageProvider("cancel"), new ImageProvider("ok"))
|
|---|
| 5262 | + .withPrefabValues(StyleSource.class, canvas, node)
|
|---|
| 5263 | + .usingGetClass().verify();
|
|---|
| 5264 | + }
|
|---|
| 5265 | +
|
|---|
| 5266 | + /**
|
|---|
| 5267 | + * Check that an instruction is in a collection of instructions, and return it
|
|---|
| 5268 | + * @param instructions The instructions to search
|
|---|
| 5269 | + * @param key The key to look for
|
|---|
| 5270 | + * @param value The expected value for the key
|
|---|
| 5271 | + */
|
|---|
| 5272 | + private void assertInInstructions(Collection<Instruction> instructions, String key, Object value) {
|
|---|
| 5273 | + // In JOSM, all Instruction objects are AssignmentInstruction objects
|
|---|
| 5274 | + Collection<Instruction.AssignmentInstruction> instructionKeys = instructions.stream()
|
|---|
| 5275 | + .filter(Instruction.AssignmentInstruction.class::isInstance)
|
|---|
| 5276 | + .map(Instruction.AssignmentInstruction.class::cast).filter(instruction -> Objects.equals(key, instruction.key))
|
|---|
| 5277 | + .collect(Collectors.toList());
|
|---|
| 5278 | + Optional<Instruction.AssignmentInstruction> instructionOptional = instructionKeys.stream()
|
|---|
| 5279 | + .filter(instruction -> Objects.equals(value, instruction.val)).findAny();
|
|---|
| 5280 | + assertTrue(instructionOptional.isPresent(), MessageFormat
|
|---|
| 5281 | + .format("Expected {0}, but got {1}", value, instructionOptional.orElse(instructionKeys.stream().findAny()
|
|---|
| 5282 | + .orElseThrow(() -> new AssertionError("No instruction with "+key+" found"))).val));
|
|---|
| 5283 | + }
|
|---|
| 5284 | +
|
|---|
| 5285 | + private static MapCSSRule getRule(MapCSSStyleSource source, String base, String subpart) {
|
|---|
| 5286 | + // We need to do a new arraylist just to avoid the occasional ConcurrentModificationException
|
|---|
| 5287 | + return new ArrayList<>(source.rules).stream().filter(rule -> rule.selectors.stream()
|
|---|
| 5288 | + .anyMatch(selector -> base.equals(selector.getBase()) && subpart.equals(selector.getSubpart().getId(null))))
|
|---|
| 5289 | + .findAny().orElse(null);
|
|---|
| 5290 | + }
|
|---|
| 5291 | +}
|
|---|
| 5292 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java
|
|---|
| 5293 | new file mode 100644
|
|---|
| 5294 | index 000000000..500b5f8b5
|
|---|
| 5295 | --- /dev/null
|
|---|
| 5296 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java
|
|---|
| 5297 | @@ -0,0 +1,188 @@
|
|---|
| 5298 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 5299 | +package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 5300 | +
|
|---|
| 5301 | +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|---|
| 5302 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 5303 | +import static org.junit.jupiter.api.Assertions.assertNull;
|
|---|
| 5304 | +import static org.junit.jupiter.api.Assertions.assertThrows;
|
|---|
| 5305 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 5306 | +
|
|---|
| 5307 | +
|
|---|
| 5308 | +import java.util.Locale;
|
|---|
| 5309 | +import java.util.stream.Collectors;
|
|---|
| 5310 | +import java.util.stream.Stream;
|
|---|
| 5311 | +
|
|---|
| 5312 | +import javax.json.Json;
|
|---|
| 5313 | +import javax.json.JsonObject;
|
|---|
| 5314 | +import javax.json.JsonValue;
|
|---|
| 5315 | +
|
|---|
| 5316 | +import org.openstreetmap.josm.data.Bounds;
|
|---|
| 5317 | +
|
|---|
| 5318 | +import nl.jqno.equalsverifier.EqualsVerifier;
|
|---|
| 5319 | +import org.junit.jupiter.api.Test;
|
|---|
| 5320 | +
|
|---|
| 5321 | +/**
|
|---|
| 5322 | + * Test class for {@link Source}
|
|---|
| 5323 | + * @author Taylor Smock
|
|---|
| 5324 | + * @since xxx
|
|---|
| 5325 | + */
|
|---|
| 5326 | +public class SourceTest {
|
|---|
| 5327 | + @Test
|
|---|
| 5328 | + void testEquals() {
|
|---|
| 5329 | + EqualsVerifier.forClass(Source.class).usingGetClass().verify();
|
|---|
| 5330 | + }
|
|---|
| 5331 | +
|
|---|
| 5332 | + @Test
|
|---|
| 5333 | + void testSimpleSources() {
|
|---|
| 5334 | + final JsonObject emptyObject = Json.createObjectBuilder().build();
|
|---|
| 5335 | + assertThrows(NullPointerException.class, () -> new Source("Test source", emptyObject));
|
|---|
| 5336 | +
|
|---|
| 5337 | + final JsonObject badTypeValue = Json.createObjectBuilder().add("type", "bad type value").build();
|
|---|
| 5338 | + assertThrows(IllegalArgumentException.class, () -> new Source("Test source", badTypeValue));
|
|---|
| 5339 | +
|
|---|
| 5340 | + // Only SourceType.{VECTOR,RASTER} are supported
|
|---|
| 5341 | + final SourceType[] supported = new SourceType[] {SourceType.VECTOR, SourceType.RASTER};
|
|---|
| 5342 | + for (SourceType type : supported) {
|
|---|
| 5343 | + final JsonObject goodSourceType = Json.createObjectBuilder().add("type", type.toString().toLowerCase(Locale.ROOT)).build();
|
|---|
| 5344 | + Source source = assertDoesNotThrow(() -> new Source(type.name(), goodSourceType));
|
|---|
| 5345 | + // Check defaults
|
|---|
| 5346 | + assertEquals(0, source.getMinZoom());
|
|---|
| 5347 | + assertEquals(22, source.getMaxZoom());
|
|---|
| 5348 | + assertEquals(type.name(), source.getName());
|
|---|
| 5349 | + assertNull(source.getAttributionText());
|
|---|
| 5350 | + assertTrue(source.getUrls().isEmpty());
|
|---|
| 5351 | + assertEquals(new Bounds(-85.051129, -180, 85.051129, 180), source.getBounds());
|
|---|
| 5352 | + }
|
|---|
| 5353 | +
|
|---|
| 5354 | + // Check that unsupported types throw
|
|---|
| 5355 | + for (SourceType type : Stream.of(SourceType.values()).filter(t -> Stream.of(supported).noneMatch(t::equals)).collect(
|
|---|
| 5356 | + Collectors.toList())) {
|
|---|
| 5357 | + final JsonObject goodSourceType = Json.createObjectBuilder().add("type", type.toString().toLowerCase(Locale.ROOT)).build();
|
|---|
| 5358 | + assertThrows(UnsupportedOperationException.class, () -> new Source(type.name(), goodSourceType));
|
|---|
| 5359 | + }
|
|---|
| 5360 | + }
|
|---|
| 5361 | +
|
|---|
| 5362 | + @Test
|
|---|
| 5363 | + void testTileJsonSpec() {
|
|---|
| 5364 | + // This isn't currently implemented, so it should throw. Mostly here to remind implementor to add tests...
|
|---|
| 5365 | + final JsonObject tileJsonSpec = Json.createObjectBuilder()
|
|---|
| 5366 | + .add("type", SourceType.VECTOR.name()).add("url", "some-random-url.com")
|
|---|
| 5367 | + .build();
|
|---|
| 5368 | + assertThrows(UnsupportedOperationException.class, () -> new Source("Test TileJson", tileJsonSpec));
|
|---|
| 5369 | + }
|
|---|
| 5370 | +
|
|---|
| 5371 | + @Test
|
|---|
| 5372 | + void testBounds() {
|
|---|
| 5373 | + // Check a "good" bounds
|
|---|
| 5374 | + final JsonObject tileJsonSpec = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("bounds",
|
|---|
| 5375 | + Json.createArrayBuilder().add(-1).add(-2).add(3).add(4)).build();
|
|---|
| 5376 | + Source source = new Source("Test Bounds[-1, -2, 3, 4]", tileJsonSpec);
|
|---|
| 5377 | + assertEquals(new Bounds(-2, -1, 4, 3), source.getBounds());
|
|---|
| 5378 | +
|
|---|
| 5379 | + // Check "bad" bounds
|
|---|
| 5380 | + final JsonObject tileJsonSpecShort = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("bounds",
|
|---|
| 5381 | + Json.createArrayBuilder().add(-1).add(-2).add(3)).build();
|
|---|
| 5382 | + IllegalArgumentException badLengthException = assertThrows(IllegalArgumentException.class,
|
|---|
| 5383 | + () -> new Source("Test Bounds[-1, -2, 3]", tileJsonSpecShort));
|
|---|
| 5384 | + assertEquals("bounds must have four values, but has 3", badLengthException.getMessage());
|
|---|
| 5385 | +
|
|---|
| 5386 | + final JsonObject tileJsonSpecLong = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("bounds",
|
|---|
| 5387 | + Json.createArrayBuilder().add(-1).add(-2).add(3).add(4).add(5)).build();
|
|---|
| 5388 | + badLengthException = assertThrows(IllegalArgumentException.class, () -> new Source("Test Bounds[-1, -2, 3, 4, 5]", tileJsonSpecLong));
|
|---|
| 5389 | + assertEquals("bounds must have four values, but has 5", badLengthException.getMessage());
|
|---|
| 5390 | + }
|
|---|
| 5391 | +
|
|---|
| 5392 | + @Test
|
|---|
| 5393 | + void testTiles() {
|
|---|
| 5394 | + // No url
|
|---|
| 5395 | + final JsonObject tileJsonSpecEmpty = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("tiles",
|
|---|
| 5396 | + JsonValue.NULL).build();
|
|---|
| 5397 | + Source source = new Source("Test Tile[]", tileJsonSpecEmpty);
|
|---|
| 5398 | + assertTrue(source.getUrls().isEmpty());
|
|---|
| 5399 | +
|
|---|
| 5400 | + // Create a tile URL
|
|---|
| 5401 | + final JsonObject tileJsonSpec = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("tiles",
|
|---|
| 5402 | + Json.createArrayBuilder().add("https://example.org/{bbox-epsg-3857}")).build();
|
|---|
| 5403 | + source = new Source("Test Tile[https://example.org/{bbox-epsg-3857}]", tileJsonSpec);
|
|---|
| 5404 | + assertEquals(1, source.getUrls().size());
|
|---|
| 5405 | + // Make certain that {bbox-epsg-3857} is replaced with the JOSM equivalent
|
|---|
| 5406 | + assertEquals("https://example.org/{bbox}", source.getUrls().get(0));
|
|---|
| 5407 | +
|
|---|
| 5408 | + // Check with invalid data
|
|---|
| 5409 | + final JsonObject tileJsonSpecBad = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("tiles",
|
|---|
| 5410 | + Json.createArrayBuilder().add(1).add("https://example.org/{bbox-epsg-3857}").add(false).add(Json.createArrayBuilder().add("hello"))
|
|---|
| 5411 | + .add(Json.createObjectBuilder().add("bad", "array"))).build();
|
|---|
| 5412 | + source = new Source("Test Tile[1, https://example.org/{bbox-epsg-3857}, false, [\"hello\"], {\"bad\": \"array\"}]", tileJsonSpecBad);
|
|---|
| 5413 | + assertEquals(1, source.getUrls().size());
|
|---|
| 5414 | + // Make certain that {bbox-epsg-3857} is replaced with the JOSM equivalent
|
|---|
| 5415 | + assertEquals("https://example.org/{bbox}", source.getUrls().get(0));
|
|---|
| 5416 | + }
|
|---|
| 5417 | +
|
|---|
| 5418 | + @Test
|
|---|
| 5419 | + void testZoom() {
|
|---|
| 5420 | + // Min zoom
|
|---|
| 5421 | + final JsonObject minZoom5 = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("minzoom",
|
|---|
| 5422 | + 5).build();
|
|---|
| 5423 | + Source source = new Source("Test Zoom[minzoom=5]", minZoom5);
|
|---|
| 5424 | + assertEquals(5, source.getMinZoom());
|
|---|
| 5425 | + assertEquals(22, source.getMaxZoom());
|
|---|
| 5426 | +
|
|---|
| 5427 | + // Negative min zoom
|
|---|
| 5428 | + final JsonObject minZoomNeg1 = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("minzoom",
|
|---|
| 5429 | + -1).build();
|
|---|
| 5430 | + source = new Source("Test Zoom[minzoom=-1]", minZoomNeg1);
|
|---|
| 5431 | + assertEquals(0, source.getMinZoom());
|
|---|
| 5432 | + assertEquals(22, source.getMaxZoom());
|
|---|
| 5433 | +
|
|---|
| 5434 | + // Max zoom
|
|---|
| 5435 | + final JsonObject maxZoom5 = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("maxzoom",
|
|---|
| 5436 | + 5).build();
|
|---|
| 5437 | + source = new Source("Test Zoom[maxzoom=5]", maxZoom5);
|
|---|
| 5438 | + assertEquals(0, source.getMinZoom());
|
|---|
| 5439 | + assertEquals(5, source.getMaxZoom());
|
|---|
| 5440 | +
|
|---|
| 5441 | + // Big Max zoom
|
|---|
| 5442 | + final JsonObject maxZoom31 = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("maxzoom",
|
|---|
| 5443 | + 31).build();
|
|---|
| 5444 | + source = new Source("Test Zoom[maxzoom=31]", maxZoom31);
|
|---|
| 5445 | + assertEquals(0, source.getMinZoom());
|
|---|
| 5446 | + assertEquals(30, source.getMaxZoom());
|
|---|
| 5447 | +
|
|---|
| 5448 | + // Negative max zoom
|
|---|
| 5449 | + final JsonObject maxZoomNeg5 = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("maxzoom",
|
|---|
| 5450 | + -5).build();
|
|---|
| 5451 | + source = new Source("Test Zoom[maxzoom=-5]", maxZoomNeg5);
|
|---|
| 5452 | + assertEquals(0, source.getMinZoom());
|
|---|
| 5453 | + assertEquals(0, source.getMaxZoom());
|
|---|
| 5454 | +
|
|---|
| 5455 | + // Min max zoom
|
|---|
| 5456 | + final JsonObject minZoom1MaxZoom5 = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("maxzoom",
|
|---|
| 5457 | + 5).add("minzoom", 1).build();
|
|---|
| 5458 | + source = new Source("Test Zoom[minzoom=1,maxzoom=5]", minZoom1MaxZoom5);
|
|---|
| 5459 | + assertEquals(1, source.getMinZoom());
|
|---|
| 5460 | + assertEquals(5, source.getMaxZoom());
|
|---|
| 5461 | + }
|
|---|
| 5462 | +
|
|---|
| 5463 | + @Test
|
|---|
| 5464 | + void testToString() {
|
|---|
| 5465 | + // Simple (no urls)
|
|---|
| 5466 | + final JsonObject noTileJsonSpec = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).build();
|
|---|
| 5467 | + Source source = new Source("Test String[]", noTileJsonSpec);
|
|---|
| 5468 | + assertEquals("Test String[]", source.toString());
|
|---|
| 5469 | +
|
|---|
| 5470 | + // With one url
|
|---|
| 5471 | + final JsonObject tileJsonSpec = Json.createObjectBuilder().add("type", SourceType.VECTOR.name()).add("tiles",
|
|---|
| 5472 | + Json.createArrayBuilder().add("https://example.org/{bbox-epsg-3857}")).build();
|
|---|
| 5473 | + source = new Source("Test String[https://example.org/{bbox-epsg-3857}]", tileJsonSpec);
|
|---|
| 5474 | + assertEquals("Test String[https://example.org/{bbox-epsg-3857}] https://example.org/{bbox}", source.toString());
|
|---|
| 5475 | +
|
|---|
| 5476 | + // With two URLs
|
|---|
| 5477 | + final JsonObject tileJsonSpecMultiple = Json.createObjectBuilder().add("type", SourceType.VECTOR.name())
|
|---|
| 5478 | + .add("tiles", Json.createArrayBuilder()
|
|---|
| 5479 | + .add("https://example.org/{bbox-epsg-3857}")
|
|---|
| 5480 | + .add("https://example.com/{bbox-epsg-3857}")).build();
|
|---|
| 5481 | + source = new Source("Test String[https://example.org/{bbox-epsg-3857},https://example.com/{bbox-epsg-3857}]", tileJsonSpecMultiple);
|
|---|
| 5482 | + assertEquals("Test String[https://example.org/{bbox-epsg-3857},https://example.com/{bbox-epsg-3857}] https://example.org/{bbox} "
|
|---|
| 5483 | + + "https://example.com/{bbox}", source.toString());
|
|---|
| 5484 | + }
|
|---|
| 5485 | +}
|
|---|
| 5486 | --
|
|---|
| 5487 | GitLab
|
|---|
| 5488 |
|
|---|
| 5489 |
|
|---|
| 5490 | From f8f62e7ec24b189bfab536c45d6b3d2c2f97c70a Mon Sep 17 00:00:00 2001
|
|---|
| 5491 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 5492 | Date: Thu, 8 Apr 2021 15:56:16 -0600
|
|---|
| 5493 | Subject: [PATCH 05/50] Vector data storage
|
|---|
| 5494 |
|
|---|
| 5495 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 5496 | ---
|
|---|
| 5497 | .../josm/data/vector/DataLayer.java | 23 +
|
|---|
| 5498 | .../josm/data/vector/DataStore.java | 126 ++++
|
|---|
| 5499 | .../josm/data/vector/VectorDataSet.java | 541 ++++++++++++++++++
|
|---|
| 5500 | .../josm/data/vector/VectorDataStore.java | 354 ++++++++++++
|
|---|
| 5501 | .../josm/data/vector/VectorNode.java | 113 ++++
|
|---|
| 5502 | .../josm/data/vector/VectorPrimitive.java | 256 +++++++++
|
|---|
| 5503 | .../josm/data/vector/VectorRelation.java | 114 ++++
|
|---|
| 5504 | .../data/vector/VectorRelationMember.java | 70 +++
|
|---|
| 5505 | .../josm/data/vector/VectorWay.java | 132 +++++
|
|---|
| 5506 | .../josm/data/vector/VectorDataSetTest.java | 141 +++++
|
|---|
| 5507 | .../josm/data/vector/VectorNodeTest.java | 153 +++++
|
|---|
| 5508 | .../josm/data/vector/VectorRelationTest.java | 45 ++
|
|---|
| 5509 | .../josm/data/vector/VectorWayTest.java | 117 ++++
|
|---|
| 5510 | 13 files changed, 2185 insertions(+)
|
|---|
| 5511 | create mode 100644 src/org/openstreetmap/josm/data/vector/DataLayer.java
|
|---|
| 5512 | create mode 100644 src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 5513 | create mode 100644 src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 5514 | create mode 100644 src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 5515 | create mode 100644 src/org/openstreetmap/josm/data/vector/VectorNode.java
|
|---|
| 5516 | create mode 100644 src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 5517 | create mode 100644 src/org/openstreetmap/josm/data/vector/VectorRelation.java
|
|---|
| 5518 | create mode 100644 src/org/openstreetmap/josm/data/vector/VectorRelationMember.java
|
|---|
| 5519 | create mode 100644 src/org/openstreetmap/josm/data/vector/VectorWay.java
|
|---|
| 5520 | create mode 100644 test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 5521 | create mode 100644 test/unit/org/openstreetmap/josm/data/vector/VectorNodeTest.java
|
|---|
| 5522 | create mode 100644 test/unit/org/openstreetmap/josm/data/vector/VectorRelationTest.java
|
|---|
| 5523 | create mode 100644 test/unit/org/openstreetmap/josm/data/vector/VectorWayTest.java
|
|---|
| 5524 |
|
|---|
| 5525 | diff --git a/src/org/openstreetmap/josm/data/vector/DataLayer.java b/src/org/openstreetmap/josm/data/vector/DataLayer.java
|
|---|
| 5526 | new file mode 100644
|
|---|
| 5527 | index 000000000..9052e5b1a
|
|---|
| 5528 | --- /dev/null
|
|---|
| 5529 | +++ b/src/org/openstreetmap/josm/data/vector/DataLayer.java
|
|---|
| 5530 | @@ -0,0 +1,23 @@
|
|---|
| 5531 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 5532 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 5533 | +
|
|---|
| 5534 | +/**
|
|---|
| 5535 | + * An interface for objects that are part of a data layer
|
|---|
| 5536 | + * @param <T> The type used to identify a layer, typically a string
|
|---|
| 5537 | + */
|
|---|
| 5538 | +public interface DataLayer<T> {
|
|---|
| 5539 | + /**
|
|---|
| 5540 | + * Get the layer
|
|---|
| 5541 | + * @return The layer
|
|---|
| 5542 | + */
|
|---|
| 5543 | + T getLayer();
|
|---|
| 5544 | +
|
|---|
| 5545 | + /**
|
|---|
| 5546 | + * Set the layer
|
|---|
| 5547 | + * @param layer The layer to set
|
|---|
| 5548 | + * @return {@code true} if the layer was set -- some objects may never change layers.
|
|---|
| 5549 | + */
|
|---|
| 5550 | + default boolean setLayer(T layer) {
|
|---|
| 5551 | + return layer != null && layer.equals(getLayer());
|
|---|
| 5552 | + }
|
|---|
| 5553 | +}
|
|---|
| 5554 | diff --git a/src/org/openstreetmap/josm/data/vector/DataStore.java b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 5555 | new file mode 100644
|
|---|
| 5556 | index 000000000..9de044f62
|
|---|
| 5557 | --- /dev/null
|
|---|
| 5558 | +++ b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 5559 | @@ -0,0 +1,126 @@
|
|---|
| 5560 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 5561 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 5562 | +
|
|---|
| 5563 | +import java.util.Collection;
|
|---|
| 5564 | +import java.util.Collections;
|
|---|
| 5565 | +import java.util.HashMap;
|
|---|
| 5566 | +import java.util.HashSet;
|
|---|
| 5567 | +import java.util.LinkedList;
|
|---|
| 5568 | +import java.util.Map;
|
|---|
| 5569 | +import java.util.Set;
|
|---|
| 5570 | +import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|---|
| 5571 | +
|
|---|
| 5572 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 5573 | +import org.openstreetmap.josm.data.DataSource;
|
|---|
| 5574 | +import org.openstreetmap.josm.data.osm.DataSet;
|
|---|
| 5575 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 5576 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 5577 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 5578 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 5579 | +import org.openstreetmap.josm.data.osm.PrimitiveId;
|
|---|
| 5580 | +import org.openstreetmap.josm.data.osm.QuadBucketPrimitiveStore;
|
|---|
| 5581 | +import org.openstreetmap.josm.data.osm.Storage;
|
|---|
| 5582 | +
|
|---|
| 5583 | +/**
|
|---|
| 5584 | + * A class that stores data (essentially a simple {@link DataSet})
|
|---|
| 5585 | + * @author Taylor Smock
|
|---|
| 5586 | + * @since xxx
|
|---|
| 5587 | + */
|
|---|
| 5588 | +class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>> {
|
|---|
| 5589 | + /**
|
|---|
| 5590 | + * This literally only exists to make {@link QuadBucketPrimitiveStore#removePrimitive} public
|
|---|
| 5591 | + *
|
|---|
| 5592 | + * @param <N> The node type
|
|---|
| 5593 | + * @param <W> The way type
|
|---|
| 5594 | + * @param <R> The relation type
|
|---|
| 5595 | + */
|
|---|
| 5596 | + static class LocalQuadBucketPrimitiveStore<N extends INode, W extends IWay<N>, R extends IRelation<?>>
|
|---|
| 5597 | + extends QuadBucketPrimitiveStore<N, W, R> {
|
|---|
| 5598 | + // Allow us to remove primitives (protected in {@link QuadBucketPrimitiveStore})
|
|---|
| 5599 | + @Override
|
|---|
| 5600 | + public void removePrimitive(IPrimitive primitive) {
|
|---|
| 5601 | + super.removePrimitive(primitive);
|
|---|
| 5602 | + }
|
|---|
| 5603 | + }
|
|---|
| 5604 | +
|
|---|
| 5605 | + protected final int zoom;
|
|---|
| 5606 | + protected final LocalQuadBucketPrimitiveStore<N, W, R> store = new LocalQuadBucketPrimitiveStore<>();
|
|---|
| 5607 | + protected final Storage<O> allPrimitives = new Storage<>(new Storage.PrimitiveIdHash(), true);
|
|---|
| 5608 | + protected final Set<Tile> addedTiles = new HashSet<>();
|
|---|
| 5609 | + protected final Map<PrimitiveId, O> primitivesMap = allPrimitives
|
|---|
| 5610 | + .foreignKey(new Storage.PrimitiveIdHash());
|
|---|
| 5611 | + protected final Collection<DataSource> dataSources = new LinkedList<>();
|
|---|
| 5612 | + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
|---|
| 5613 | +
|
|---|
| 5614 | + DataStore(int zoom) {
|
|---|
| 5615 | + this.zoom = zoom;
|
|---|
| 5616 | + }
|
|---|
| 5617 | +
|
|---|
| 5618 | + public int getZoom() {
|
|---|
| 5619 | + return this.zoom;
|
|---|
| 5620 | + }
|
|---|
| 5621 | +
|
|---|
| 5622 | + public QuadBucketPrimitiveStore<N, W, R> getStore() {
|
|---|
| 5623 | + return this.store;
|
|---|
| 5624 | + }
|
|---|
| 5625 | +
|
|---|
| 5626 | + public Storage<O> getAllPrimitives() {
|
|---|
| 5627 | + return this.allPrimitives;
|
|---|
| 5628 | + }
|
|---|
| 5629 | +
|
|---|
| 5630 | + public Map<PrimitiveId, O> getPrimitivesMap() {
|
|---|
| 5631 | + if (this.readWriteLock.isWriteLocked()) {
|
|---|
| 5632 | + return new HashMap<>(this.primitivesMap);
|
|---|
| 5633 | + }
|
|---|
| 5634 | + return this.primitivesMap;
|
|---|
| 5635 | + }
|
|---|
| 5636 | +
|
|---|
| 5637 | + public Collection<DataSource> getDataSources() {
|
|---|
| 5638 | + return Collections.unmodifiableCollection(dataSources);
|
|---|
| 5639 | + }
|
|---|
| 5640 | +
|
|---|
| 5641 | + /**
|
|---|
| 5642 | + * Add a datasource to this data set
|
|---|
| 5643 | + * @param dataSource The data soure to add
|
|---|
| 5644 | + */
|
|---|
| 5645 | + public void addDataSource(DataSource dataSource) {
|
|---|
| 5646 | + this.dataSources.add(dataSource);
|
|---|
| 5647 | + }
|
|---|
| 5648 | +
|
|---|
| 5649 | + /**
|
|---|
| 5650 | + * Add a primitive to this dataset
|
|---|
| 5651 | + * @param primitive The primitive to remove
|
|---|
| 5652 | + */
|
|---|
| 5653 | + @SuppressWarnings("squid:S2445")
|
|---|
| 5654 | + protected void removePrimitive(O primitive) {
|
|---|
| 5655 | + if (primitive == null) {
|
|---|
| 5656 | + return;
|
|---|
| 5657 | + }
|
|---|
| 5658 | + // This is deliberate -- attempting to remove the primitive twice causes issues
|
|---|
| 5659 | + synchronized (primitive) {
|
|---|
| 5660 | + if (this.allPrimitives.contains(primitive)) {
|
|---|
| 5661 | + this.store.removePrimitive(primitive);
|
|---|
| 5662 | + this.allPrimitives.remove(primitive);
|
|---|
| 5663 | + this.primitivesMap.remove(primitive.getPrimitiveId());
|
|---|
| 5664 | + }
|
|---|
| 5665 | + }
|
|---|
| 5666 | + }
|
|---|
| 5667 | +
|
|---|
| 5668 | + /**
|
|---|
| 5669 | + * Add a primitive to this dataset
|
|---|
| 5670 | + * @param primitive The primitive to add
|
|---|
| 5671 | + */
|
|---|
| 5672 | + protected void addPrimitive(O primitive) {
|
|---|
| 5673 | + this.store.addPrimitive(primitive);
|
|---|
| 5674 | + this.allPrimitives.add(primitive);
|
|---|
| 5675 | + this.primitivesMap.put(primitive.getPrimitiveId(), primitive);
|
|---|
| 5676 | + }
|
|---|
| 5677 | +
|
|---|
| 5678 | + /**
|
|---|
| 5679 | + * Get the read/write lock for this dataset
|
|---|
| 5680 | + * @return The read/write lock
|
|---|
| 5681 | + */
|
|---|
| 5682 | + protected ReentrantReadWriteLock getReadWriteLock() {
|
|---|
| 5683 | + return this.readWriteLock;
|
|---|
| 5684 | + }
|
|---|
| 5685 | +}
|
|---|
| 5686 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 5687 | new file mode 100644
|
|---|
| 5688 | index 000000000..dfa9334a3
|
|---|
| 5689 | --- /dev/null
|
|---|
| 5690 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 5691 | @@ -0,0 +1,541 @@
|
|---|
| 5692 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 5693 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 5694 | +
|
|---|
| 5695 | +import java.util.ArrayList;
|
|---|
| 5696 | +import java.util.Arrays;
|
|---|
| 5697 | +import java.util.Collection;
|
|---|
| 5698 | +import java.util.Collections;
|
|---|
| 5699 | +import java.util.HashSet;
|
|---|
| 5700 | +import java.util.List;
|
|---|
| 5701 | +import java.util.Map;
|
|---|
| 5702 | +import java.util.Objects;
|
|---|
| 5703 | +import java.util.Optional;
|
|---|
| 5704 | +import java.util.concurrent.ConcurrentHashMap;
|
|---|
| 5705 | +import java.util.concurrent.locks.Lock;
|
|---|
| 5706 | +import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|---|
| 5707 | +import java.util.function.Predicate;
|
|---|
| 5708 | +import java.util.function.Supplier;
|
|---|
| 5709 | +import java.util.stream.Collectors;
|
|---|
| 5710 | +import java.util.stream.IntStream;
|
|---|
| 5711 | +import java.util.stream.Stream;
|
|---|
| 5712 | +
|
|---|
| 5713 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 5714 | +import org.openstreetmap.josm.data.DataSource;
|
|---|
| 5715 | +import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 5716 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 5717 | +import org.openstreetmap.josm.data.osm.DataSelectionListener;
|
|---|
| 5718 | +import org.openstreetmap.josm.data.osm.DownloadPolicy;
|
|---|
| 5719 | +import org.openstreetmap.josm.data.osm.HighlightUpdateListener;
|
|---|
| 5720 | +import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 5721 | +import org.openstreetmap.josm.data.osm.PrimitiveId;
|
|---|
| 5722 | +import org.openstreetmap.josm.data.osm.UploadPolicy;
|
|---|
| 5723 | +import org.openstreetmap.josm.data.osm.WaySegment;
|
|---|
| 5724 | +import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 5725 | +import org.openstreetmap.josm.tools.ListenerList;
|
|---|
| 5726 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 5727 | +import org.openstreetmap.josm.tools.SubclassFilteredCollection;
|
|---|
| 5728 | +
|
|---|
| 5729 | +/**
|
|---|
| 5730 | + * A data class for Vector Data
|
|---|
| 5731 | + *
|
|---|
| 5732 | + * @author Taylor Smock
|
|---|
| 5733 | + * @since xxx
|
|---|
| 5734 | + */
|
|---|
| 5735 | +public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, VectorWay, VectorRelation> {
|
|---|
| 5736 | + // Note: In Java 8, computeIfAbsent is blocking for both pre-existing and new values. In Java 9, it is only blocking
|
|---|
| 5737 | + // for new values (perf increase). See JDK-8161372 for more info.
|
|---|
| 5738 | + private final Map<Integer, VectorDataStore> dataStoreMap = new ConcurrentHashMap<>();
|
|---|
| 5739 | + private final Collection<PrimitiveId> selected = new HashSet<>();
|
|---|
| 5740 | + // Both of these listener lists are useless, since they expect OsmPrimitives at this time
|
|---|
| 5741 | + private final ListenerList<HighlightUpdateListener> highlightUpdateListenerListenerList = ListenerList.create();
|
|---|
| 5742 | + private final ListenerList<DataSelectionListener> dataSelectionListenerListenerList = ListenerList.create();
|
|---|
| 5743 | + private boolean lock = true;
|
|---|
| 5744 | + private String name;
|
|---|
| 5745 | + private short mappaintCacheIdx = 1;
|
|---|
| 5746 | +
|
|---|
| 5747 | + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
|---|
| 5748 | +
|
|---|
| 5749 | + /**
|
|---|
| 5750 | + * The distance to consider nodes duplicates -- mostly a memory saving measure.
|
|---|
| 5751 | + * 0.000_000_1 ~1.2 cm (+- 5.57 mm)
|
|---|
| 5752 | + * Descriptions from <a href="https://xkcd.com/2170/">https://xkcd.com/2170/</a>
|
|---|
| 5753 | + * Notes on <a href="https://wiki.openstreetmap.org/wiki/Node">https://wiki.openstreetmap.org/wiki/Node</a> indicate
|
|---|
| 5754 | + * that IEEE 32-bit floats should not be used at high longitude (0.000_01 precision)
|
|---|
| 5755 | + */
|
|---|
| 5756 | + protected static final float DUPE_NODE_DISTANCE = 0.000_000_1f;
|
|---|
| 5757 | +
|
|---|
| 5758 | + /**
|
|---|
| 5759 | + * The current zoom we are getting/adding to
|
|---|
| 5760 | + */
|
|---|
| 5761 | + private int zoom;
|
|---|
| 5762 | + /**
|
|---|
| 5763 | + * Default to normal download policy
|
|---|
| 5764 | + */
|
|---|
| 5765 | + private DownloadPolicy downloadPolicy = DownloadPolicy.NORMAL;
|
|---|
| 5766 | + /**
|
|---|
| 5767 | + * Default to a blocked upload policy
|
|---|
| 5768 | + */
|
|---|
| 5769 | + private UploadPolicy uploadPolicy = UploadPolicy.BLOCKED;
|
|---|
| 5770 | + /**
|
|---|
| 5771 | + * The paint style for this layer
|
|---|
| 5772 | + */
|
|---|
| 5773 | + private ElemStyles styles;
|
|---|
| 5774 | +
|
|---|
| 5775 | + @Override
|
|---|
| 5776 | + public Collection<DataSource> getDataSources() {
|
|---|
| 5777 | + final int currentZoom = this.zoom;
|
|---|
| 5778 | + final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 5779 | + return dataStore.getDataSources();
|
|---|
| 5780 | + }
|
|---|
| 5781 | +
|
|---|
| 5782 | + /**
|
|---|
| 5783 | + * Add a data source
|
|---|
| 5784 | + *
|
|---|
| 5785 | + * @param currentZoom the zoom
|
|---|
| 5786 | + * @param dataSource The datasource to add at the zoom level
|
|---|
| 5787 | + */
|
|---|
| 5788 | + public void addDataSource(int currentZoom, DataSource dataSource) {
|
|---|
| 5789 | + final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 5790 | + dataStore.addDataSource(dataSource);
|
|---|
| 5791 | + }
|
|---|
| 5792 | +
|
|---|
| 5793 | + @Override
|
|---|
| 5794 | + public void lock() {
|
|---|
| 5795 | + this.lock = true;
|
|---|
| 5796 | + }
|
|---|
| 5797 | +
|
|---|
| 5798 | + @Override
|
|---|
| 5799 | + public void unlock() {
|
|---|
| 5800 | + this.lock = false;
|
|---|
| 5801 | + }
|
|---|
| 5802 | +
|
|---|
| 5803 | + @Override
|
|---|
| 5804 | + public boolean isLocked() {
|
|---|
| 5805 | + return this.lock;
|
|---|
| 5806 | + }
|
|---|
| 5807 | +
|
|---|
| 5808 | + @Override
|
|---|
| 5809 | + public String getVersion() {
|
|---|
| 5810 | + return "8"; // TODO get this dynamically. Not critical, as this is currently the _only_ version.
|
|---|
| 5811 | + }
|
|---|
| 5812 | +
|
|---|
| 5813 | + @Override
|
|---|
| 5814 | + public String getName() {
|
|---|
| 5815 | + return this.name;
|
|---|
| 5816 | + }
|
|---|
| 5817 | +
|
|---|
| 5818 | + @Override
|
|---|
| 5819 | + public void setName(String name) {
|
|---|
| 5820 | + this.name = name;
|
|---|
| 5821 | + }
|
|---|
| 5822 | +
|
|---|
| 5823 | + @Override
|
|---|
| 5824 | + public void addPrimitive(VectorPrimitive primitive) {
|
|---|
| 5825 | + primitive.setDataSet(this);
|
|---|
| 5826 | + final int currentZoom = this.zoom;
|
|---|
| 5827 | + final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 5828 | + tryWrite(dataStore, () -> dataStore.addPrimitive(primitive));
|
|---|
| 5829 | + }
|
|---|
| 5830 | +
|
|---|
| 5831 | + /**
|
|---|
| 5832 | + * Remove a primitive from this dataset
|
|---|
| 5833 | + *
|
|---|
| 5834 | + * @param primitive The primitive to remove
|
|---|
| 5835 | + */
|
|---|
| 5836 | + protected void removePrimitive(VectorPrimitive primitive) {
|
|---|
| 5837 | + if (primitive.getDataSet() == this) {
|
|---|
| 5838 | + primitive.setDataSet(null);
|
|---|
| 5839 | + this.dataStoreMap.values()
|
|---|
| 5840 | + .forEach(vectorDataStore -> tryWrite(vectorDataStore, () -> vectorDataStore.removePrimitive(primitive)));
|
|---|
| 5841 | + }
|
|---|
| 5842 | + }
|
|---|
| 5843 | +
|
|---|
| 5844 | + @Override
|
|---|
| 5845 | + public void clear() {
|
|---|
| 5846 | + synchronized (this.dataStoreMap) {
|
|---|
| 5847 | + this.dataStoreMap.clear();
|
|---|
| 5848 | + }
|
|---|
| 5849 | + }
|
|---|
| 5850 | +
|
|---|
| 5851 | + @Override
|
|---|
| 5852 | + public List<VectorNode> searchNodes(BBox bbox) {
|
|---|
| 5853 | + return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.searchNodes(bbox))
|
|---|
| 5854 | + .orElseGet(Collections::emptyList);
|
|---|
| 5855 | + }
|
|---|
| 5856 | +
|
|---|
| 5857 | + @Override
|
|---|
| 5858 | + public boolean containsNode(VectorNode vectorNode) {
|
|---|
| 5859 | + return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.containsNode(vectorNode)).orElse(false);
|
|---|
| 5860 | + }
|
|---|
| 5861 | +
|
|---|
| 5862 | + @Override
|
|---|
| 5863 | + public List<VectorWay> searchWays(BBox bbox) {
|
|---|
| 5864 | + return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.searchWays(bbox))
|
|---|
| 5865 | + .orElseGet(Collections::emptyList);
|
|---|
| 5866 | + }
|
|---|
| 5867 | +
|
|---|
| 5868 | + @Override
|
|---|
| 5869 | + public boolean containsWay(VectorWay vectorWay) {
|
|---|
| 5870 | + return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.containsWay(vectorWay)).orElse(false);
|
|---|
| 5871 | + }
|
|---|
| 5872 | +
|
|---|
| 5873 | + @Override
|
|---|
| 5874 | + public List<VectorRelation> searchRelations(BBox bbox) {
|
|---|
| 5875 | + return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.searchRelations(bbox))
|
|---|
| 5876 | + .orElseGet(Collections::emptyList);
|
|---|
| 5877 | + }
|
|---|
| 5878 | +
|
|---|
| 5879 | + @Override
|
|---|
| 5880 | + public boolean containsRelation(VectorRelation vectorRelation) {
|
|---|
| 5881 | + return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.containsRelation(vectorRelation)).orElse(false);
|
|---|
| 5882 | + }
|
|---|
| 5883 | +
|
|---|
| 5884 | + @Override
|
|---|
| 5885 | + public VectorPrimitive getPrimitiveById(PrimitiveId primitiveId) {
|
|---|
| 5886 | + return this.getBestZoomDataStore().map(VectorDataStore::getPrimitivesMap).map(m -> m .get(primitiveId)).orElse(null);
|
|---|
| 5887 | + }
|
|---|
| 5888 | +
|
|---|
| 5889 | + // The last return statement is "unchecked", even though it is literally the same as the previous return, except
|
|---|
| 5890 | + // as an optional.
|
|---|
| 5891 | + @SuppressWarnings("unchecked")
|
|---|
| 5892 | + @Override
|
|---|
| 5893 | + public <T extends VectorPrimitive> Collection<T> getPrimitives(
|
|---|
| 5894 | + Predicate<? super VectorPrimitive> predicate) {
|
|---|
| 5895 | + final VectorDataStore dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 5896 | + if (dataStore == null) {
|
|---|
| 5897 | + return Collections.emptyList();
|
|---|
| 5898 | + }
|
|---|
| 5899 | +
|
|---|
| 5900 | + if (dataStore.getReadWriteLock().isWriteLocked()) {
|
|---|
| 5901 | + return new SubclassFilteredCollection<>(new HashSet<>(dataStore.getAllPrimitives()), predicate);
|
|---|
| 5902 | + }
|
|---|
| 5903 | + return (Collection<T>) tryRead(dataStore, () -> new SubclassFilteredCollection<>(dataStore.getAllPrimitives(), predicate))
|
|---|
| 5904 | + // Throw an NPE if we don't have a collection (this should never happen, so if it does, _something_ is wrong)
|
|---|
| 5905 | + .orElseThrow(NullPointerException::new);
|
|---|
| 5906 | + }
|
|---|
| 5907 | +
|
|---|
| 5908 | + @Override
|
|---|
| 5909 | + public Collection<VectorNode> getNodes() {
|
|---|
| 5910 | + return this.getPrimitives(VectorNode.class::isInstance);
|
|---|
| 5911 | + }
|
|---|
| 5912 | +
|
|---|
| 5913 | + @Override
|
|---|
| 5914 | + public Collection<VectorWay> getWays() {
|
|---|
| 5915 | + return this.getPrimitives(VectorWay.class::isInstance);
|
|---|
| 5916 | + }
|
|---|
| 5917 | +
|
|---|
| 5918 | + @Override
|
|---|
| 5919 | + public Collection<VectorRelation> getRelations() {
|
|---|
| 5920 | + return this.getPrimitives(VectorRelation.class::isInstance);
|
|---|
| 5921 | + }
|
|---|
| 5922 | +
|
|---|
| 5923 | + @Override
|
|---|
| 5924 | + public DownloadPolicy getDownloadPolicy() {
|
|---|
| 5925 | + return this.downloadPolicy;
|
|---|
| 5926 | + }
|
|---|
| 5927 | +
|
|---|
| 5928 | + @Override
|
|---|
| 5929 | + public void setDownloadPolicy(DownloadPolicy downloadPolicy) {
|
|---|
| 5930 | + this.downloadPolicy = downloadPolicy;
|
|---|
| 5931 | + }
|
|---|
| 5932 | +
|
|---|
| 5933 | + @Override
|
|---|
| 5934 | + public UploadPolicy getUploadPolicy() {
|
|---|
| 5935 | + return this.uploadPolicy;
|
|---|
| 5936 | + }
|
|---|
| 5937 | +
|
|---|
| 5938 | + @Override
|
|---|
| 5939 | + public void setUploadPolicy(UploadPolicy uploadPolicy) {
|
|---|
| 5940 | + this.uploadPolicy = uploadPolicy;
|
|---|
| 5941 | + }
|
|---|
| 5942 | +
|
|---|
| 5943 | + /**
|
|---|
| 5944 | + * Get the current Read/Write lock
|
|---|
| 5945 | + * @implNote This changes based off of zoom level. Please do not use this in a finally block
|
|---|
| 5946 | + * @return The current read/write lock
|
|---|
| 5947 | + */
|
|---|
| 5948 | + @Override
|
|---|
| 5949 | + public Lock getReadLock() {
|
|---|
| 5950 | + return getBestZoomDataStore().map(VectorDataStore::getReadWriteLock).map(ReentrantReadWriteLock::readLock)
|
|---|
| 5951 | + .orElse(this.readWriteLock.readLock());
|
|---|
| 5952 | + }
|
|---|
| 5953 | +
|
|---|
| 5954 | + @Override
|
|---|
| 5955 | + public Collection<WaySegment> getHighlightedVirtualNodes() {
|
|---|
| 5956 | + // TODO? This requires a change to WaySegment so that it isn't Way/Node specific
|
|---|
| 5957 | + return Collections.emptyList();
|
|---|
| 5958 | + }
|
|---|
| 5959 | +
|
|---|
| 5960 | + @Override
|
|---|
| 5961 | + public void setHighlightedVirtualNodes(Collection<WaySegment> waySegments) {
|
|---|
| 5962 | + // TODO? This requires a change to WaySegment so that it isn't Way/Node specific
|
|---|
| 5963 | + }
|
|---|
| 5964 | +
|
|---|
| 5965 | + @Override
|
|---|
| 5966 | + public Collection<WaySegment> getHighlightedWaySegments() {
|
|---|
| 5967 | + // TODO? This requires a change to WaySegment so that it isn't Way/Node specific
|
|---|
| 5968 | + return Collections.emptyList();
|
|---|
| 5969 | + }
|
|---|
| 5970 | +
|
|---|
| 5971 | + @Override
|
|---|
| 5972 | + public void setHighlightedWaySegments(Collection<WaySegment> waySegments) {
|
|---|
| 5973 | + // TODO? This requires a change to WaySegment so that it isn't Way/Node specific
|
|---|
| 5974 | + }
|
|---|
| 5975 | +
|
|---|
| 5976 | + @Override
|
|---|
| 5977 | + public void addHighlightUpdateListener(HighlightUpdateListener listener) {
|
|---|
| 5978 | + this.highlightUpdateListenerListenerList.addListener(listener);
|
|---|
| 5979 | + }
|
|---|
| 5980 | +
|
|---|
| 5981 | + @Override
|
|---|
| 5982 | + public void removeHighlightUpdateListener(HighlightUpdateListener listener) {
|
|---|
| 5983 | + this.highlightUpdateListenerListenerList.removeListener(listener);
|
|---|
| 5984 | + }
|
|---|
| 5985 | +
|
|---|
| 5986 | + @Override
|
|---|
| 5987 | + public Collection<VectorPrimitive> getAllSelected() {
|
|---|
| 5988 | + final Optional<VectorDataStore> dataStore = this.getBestZoomDataStore();
|
|---|
| 5989 | + return dataStore.map(vectorDataStore -> vectorDataStore.getAllPrimitives().stream()
|
|---|
| 5990 | + .filter(primitive -> this.selected.contains(primitive.getPrimitiveId()))
|
|---|
| 5991 | + .collect(Collectors.toList())).orElse(Collections.emptyList());
|
|---|
| 5992 | + }
|
|---|
| 5993 | +
|
|---|
| 5994 | + /**
|
|---|
| 5995 | + * Get the best zoom datastore
|
|---|
| 5996 | + * @return A datastore with data, or {@code null} if no good datastore exists.
|
|---|
| 5997 | + */
|
|---|
| 5998 | + private Optional<VectorDataStore> getBestZoomDataStore() {
|
|---|
| 5999 | + final int currentZoom = this.zoom;
|
|---|
| 6000 | + if (this.dataStoreMap.containsKey(currentZoom)) {
|
|---|
| 6001 | + return Optional.of(this.dataStoreMap.get(currentZoom));
|
|---|
| 6002 | + }
|
|---|
| 6003 | + // Check up to two zooms higher (may cause perf hit)
|
|---|
| 6004 | + for (int tZoom = currentZoom + 1; tZoom < currentZoom + 3; tZoom++) {
|
|---|
| 6005 | + if (this.dataStoreMap.containsKey(tZoom)) {
|
|---|
| 6006 | + return Optional.of(this.dataStoreMap.get(tZoom));
|
|---|
| 6007 | + }
|
|---|
| 6008 | + }
|
|---|
| 6009 | + // Return *any* lower zoom data (shouldn't cause a perf hit...)
|
|---|
| 6010 | + for (int tZoom = currentZoom - 1; tZoom >= 0; tZoom--) {
|
|---|
| 6011 | + if (this.dataStoreMap.containsKey(tZoom)) {
|
|---|
| 6012 | + return Optional.of(this.dataStoreMap.get(tZoom));
|
|---|
| 6013 | + }
|
|---|
| 6014 | + }
|
|---|
| 6015 | + // Check higher level zooms. May cause perf issues if selected datastore has a lot of data.
|
|---|
| 6016 | + for (int tZoom = currentZoom + 3; tZoom < 34; tZoom++) {
|
|---|
| 6017 | + if (this.dataStoreMap.containsKey(tZoom)) {
|
|---|
| 6018 | + return Optional.of(this.dataStoreMap.get(tZoom));
|
|---|
| 6019 | + }
|
|---|
| 6020 | + }
|
|---|
| 6021 | + return Optional.empty();
|
|---|
| 6022 | + }
|
|---|
| 6023 | +
|
|---|
| 6024 | + @Override
|
|---|
| 6025 | + public boolean selectionEmpty() {
|
|---|
| 6026 | + return this.selected.isEmpty();
|
|---|
| 6027 | + }
|
|---|
| 6028 | +
|
|---|
| 6029 | + @Override
|
|---|
| 6030 | + public boolean isSelected(VectorPrimitive osm) {
|
|---|
| 6031 | + return this.selected.contains(osm.getPrimitiveId());
|
|---|
| 6032 | + }
|
|---|
| 6033 | +
|
|---|
| 6034 | + @Override
|
|---|
| 6035 | + public void toggleSelected(Collection<? extends PrimitiveId> osm) {
|
|---|
| 6036 | + this.toggleSelectedImpl(osm.stream());
|
|---|
| 6037 | + }
|
|---|
| 6038 | +
|
|---|
| 6039 | + @Override
|
|---|
| 6040 | + public void toggleSelected(PrimitiveId... osm) {
|
|---|
| 6041 | + this.toggleSelectedImpl(Stream.of(osm));
|
|---|
| 6042 | + }
|
|---|
| 6043 | +
|
|---|
| 6044 | + private void toggleSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 6045 | + osm.forEach(primitiveId -> {
|
|---|
| 6046 | + if (this.selected.contains(primitiveId)) {
|
|---|
| 6047 | + this.selected.remove(primitiveId);
|
|---|
| 6048 | + } else {
|
|---|
| 6049 | + this.selected.add(primitiveId);
|
|---|
| 6050 | + }
|
|---|
| 6051 | + });
|
|---|
| 6052 | + }
|
|---|
| 6053 | +
|
|---|
| 6054 | + @Override
|
|---|
| 6055 | + public void setSelected(Collection<? extends PrimitiveId> selection) {
|
|---|
| 6056 | + this.setSelectedImpl(selection.stream());
|
|---|
| 6057 | + }
|
|---|
| 6058 | +
|
|---|
| 6059 | + @Override
|
|---|
| 6060 | + public void setSelected(PrimitiveId... osm) {
|
|---|
| 6061 | + this.setSelectedImpl(Stream.of(osm));
|
|---|
| 6062 | + }
|
|---|
| 6063 | +
|
|---|
| 6064 | + private void setSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 6065 | + this.selected.clear();
|
|---|
| 6066 | + osm.forEach(this.selected::add);
|
|---|
| 6067 | + }
|
|---|
| 6068 | +
|
|---|
| 6069 | + @Override
|
|---|
| 6070 | + public void addSelected(Collection<? extends PrimitiveId> selection) {
|
|---|
| 6071 | + this.addSelectedImpl(selection.stream());
|
|---|
| 6072 | + }
|
|---|
| 6073 | +
|
|---|
| 6074 | + @Override
|
|---|
| 6075 | + public void addSelected(PrimitiveId... osm) {
|
|---|
| 6076 | + this.addSelectedImpl(Stream.of(osm));
|
|---|
| 6077 | + }
|
|---|
| 6078 | +
|
|---|
| 6079 | + private void addSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 6080 | + osm.forEach(this.selected::add);
|
|---|
| 6081 | + }
|
|---|
| 6082 | +
|
|---|
| 6083 | + @Override
|
|---|
| 6084 | + public void clearSelection(PrimitiveId... osm) {
|
|---|
| 6085 | + this.clearSelectionImpl(Stream.of(osm));
|
|---|
| 6086 | + }
|
|---|
| 6087 | +
|
|---|
| 6088 | + @Override
|
|---|
| 6089 | + public void clearSelection(Collection<? extends PrimitiveId> list) {
|
|---|
| 6090 | + this.clearSelectionImpl(list.stream());
|
|---|
| 6091 | + }
|
|---|
| 6092 | +
|
|---|
| 6093 | + @Override
|
|---|
| 6094 | + public void clearSelection() {
|
|---|
| 6095 | + this.clearSelectionImpl(new ArrayList<>(this.selected).stream());
|
|---|
| 6096 | + }
|
|---|
| 6097 | +
|
|---|
| 6098 | + private void clearSelectionImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 6099 | + osm.forEach(this.selected::remove);
|
|---|
| 6100 | + }
|
|---|
| 6101 | +
|
|---|
| 6102 | + @Override
|
|---|
| 6103 | + public void addSelectionListener(DataSelectionListener listener) {
|
|---|
| 6104 | + this.dataSelectionListenerListenerList.addListener(listener);
|
|---|
| 6105 | + }
|
|---|
| 6106 | +
|
|---|
| 6107 | + @Override
|
|---|
| 6108 | + public void removeSelectionListener(DataSelectionListener listener) {
|
|---|
| 6109 | + this.dataSelectionListenerListenerList.removeListener(listener);
|
|---|
| 6110 | + }
|
|---|
| 6111 | +
|
|---|
| 6112 | + public short getMappaintCacheIndex() {
|
|---|
| 6113 | + return this.mappaintCacheIdx;
|
|---|
| 6114 | + }
|
|---|
| 6115 | +
|
|---|
| 6116 | + @Override
|
|---|
| 6117 | + public void clearMappaintCache() {
|
|---|
| 6118 | + this.mappaintCacheIdx++;
|
|---|
| 6119 | + }
|
|---|
| 6120 | +
|
|---|
| 6121 | + public void setZoom(int zoom) {
|
|---|
| 6122 | + if (zoom == this.zoom) {
|
|---|
| 6123 | + return; // Do nothing -- zoom isn't actually changing
|
|---|
| 6124 | + }
|
|---|
| 6125 | + this.zoom = zoom;
|
|---|
| 6126 | + this.clearMappaintCache();
|
|---|
| 6127 | + final int[] nearestZoom = {-1, -1, -1, -1};
|
|---|
| 6128 | + nearestZoom[0] = zoom;
|
|---|
| 6129 | + // Create a new list to avoid concurrent modification issues
|
|---|
| 6130 | + synchronized (this.dataStoreMap) {
|
|---|
| 6131 | + final int[] keys = new ArrayList<>(this.dataStoreMap.keySet()).stream().filter(Objects::nonNull)
|
|---|
| 6132 | + .mapToInt(Integer::intValue).sorted().toArray();
|
|---|
| 6133 | + final int index;
|
|---|
| 6134 | + if (this.dataStoreMap.containsKey(zoom)) {
|
|---|
| 6135 | + index = Arrays.binarySearch(keys, zoom);
|
|---|
| 6136 | + } else {
|
|---|
| 6137 | + // (-(insertion point) - 1) = return -> insertion point = -(return + 1)
|
|---|
| 6138 | + index = -(Arrays.binarySearch(keys, zoom) + 1);
|
|---|
| 6139 | + }
|
|---|
| 6140 | + if (index > 0) {
|
|---|
| 6141 | + nearestZoom[1] = keys[index - 1];
|
|---|
| 6142 | + }
|
|---|
| 6143 | + if (index < keys.length - 2) {
|
|---|
| 6144 | + nearestZoom[2] = keys[index + 1];
|
|---|
| 6145 | + }
|
|---|
| 6146 | +
|
|---|
| 6147 | + nearestZoom[3] = this.getBestZoomDataStore().map(VectorDataStore::getZoom).orElse(-1);
|
|---|
| 6148 | + IntStream.of(keys).filter(key -> IntStream.of(nearestZoom).noneMatch(zoomKey -> zoomKey == key))
|
|---|
| 6149 | + .mapToObj(this.dataStoreMap::get).forEach(VectorDataStore::destroy);
|
|---|
| 6150 | + IntStream.of(keys).filter(key -> IntStream.of(nearestZoom).noneMatch(zoomKey -> zoomKey == key))
|
|---|
| 6151 | + .forEach(this.dataStoreMap::remove);
|
|---|
| 6152 | + }
|
|---|
| 6153 | + }
|
|---|
| 6154 | +
|
|---|
| 6155 | + public int getZoom() {
|
|---|
| 6156 | + return this.zoom;
|
|---|
| 6157 | + }
|
|---|
| 6158 | +
|
|---|
| 6159 | + /**
|
|---|
| 6160 | + * Add tile data to this dataset
|
|---|
| 6161 | + * @param tile The tile to add
|
|---|
| 6162 | + * @param <T> The tile type
|
|---|
| 6163 | + */
|
|---|
| 6164 | + public <T extends Tile & VectorTile> void addTileData(T tile) {
|
|---|
| 6165 | + final int currentZoom = tile.getZoom();
|
|---|
| 6166 | + // computeIfAbsent should be thread safe (ConcurrentHashMap indicates it is, anyway)
|
|---|
| 6167 | + final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 6168 | + tryWrite(dataStore, () -> dataStore.addTile(tile));
|
|---|
| 6169 | + }
|
|---|
| 6170 | +
|
|---|
| 6171 | + /**
|
|---|
| 6172 | + * Try to read something (here to avoid boilerplate)
|
|---|
| 6173 | + *
|
|---|
| 6174 | + * @param supplier The reading function
|
|---|
| 6175 | + * @param <T> The return type
|
|---|
| 6176 | + * @return The optional return
|
|---|
| 6177 | + */
|
|---|
| 6178 | + private static <T> Optional<T> tryRead(VectorDataStore dataStore, Supplier<T> supplier) {
|
|---|
| 6179 | + try {
|
|---|
| 6180 | + dataStore.getReadWriteLock().readLock().lockInterruptibly();
|
|---|
| 6181 | + return Optional.ofNullable(supplier.get());
|
|---|
| 6182 | + } catch (InterruptedException e) {
|
|---|
| 6183 | + Logging.error(e);
|
|---|
| 6184 | + Thread.currentThread().interrupt();
|
|---|
| 6185 | + } finally {
|
|---|
| 6186 | + dataStore.getReadWriteLock().readLock().unlock();
|
|---|
| 6187 | + }
|
|---|
| 6188 | + return Optional.empty();
|
|---|
| 6189 | + }
|
|---|
| 6190 | +
|
|---|
| 6191 | + /**
|
|---|
| 6192 | + * Try to write something (here to avoid boilerplate)
|
|---|
| 6193 | + *
|
|---|
| 6194 | + * @param runnable The writing function
|
|---|
| 6195 | + */
|
|---|
| 6196 | + private static void tryWrite(VectorDataStore dataStore, Runnable runnable) {
|
|---|
| 6197 | + try {
|
|---|
| 6198 | + dataStore.getReadWriteLock().writeLock().lockInterruptibly();
|
|---|
| 6199 | + runnable.run();
|
|---|
| 6200 | + } catch (InterruptedException e) {
|
|---|
| 6201 | + Logging.error(e);
|
|---|
| 6202 | + Thread.currentThread().interrupt();
|
|---|
| 6203 | + } finally {
|
|---|
| 6204 | + if (dataStore.getReadWriteLock().isWriteLockedByCurrentThread()) {
|
|---|
| 6205 | + dataStore.getReadWriteLock().writeLock().unlock();
|
|---|
| 6206 | + }
|
|---|
| 6207 | + }
|
|---|
| 6208 | + }
|
|---|
| 6209 | +
|
|---|
| 6210 | + /**
|
|---|
| 6211 | + * Get the styles for this layer
|
|---|
| 6212 | + *
|
|---|
| 6213 | + * @return The styles
|
|---|
| 6214 | + */
|
|---|
| 6215 | + public ElemStyles getStyles() {
|
|---|
| 6216 | + return this.styles;
|
|---|
| 6217 | + }
|
|---|
| 6218 | +
|
|---|
| 6219 | + /**
|
|---|
| 6220 | + * Set the styles for this layer
|
|---|
| 6221 | + * @param styles The styles to set for this layer
|
|---|
| 6222 | + */
|
|---|
| 6223 | + public void setStyles(Collection<ElemStyles> styles) {
|
|---|
| 6224 | + if (styles.size() == 1) {
|
|---|
| 6225 | + this.styles = styles.iterator().next();
|
|---|
| 6226 | + } else if (!styles.isEmpty()) {
|
|---|
| 6227 | + this.styles = new ElemStyles(styles.stream().flatMap(style -> style.getStyleSources().stream()).collect(Collectors.toList()));
|
|---|
| 6228 | + } else {
|
|---|
| 6229 | + this.styles = null;
|
|---|
| 6230 | + }
|
|---|
| 6231 | + }
|
|---|
| 6232 | +}
|
|---|
| 6233 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 6234 | new file mode 100644
|
|---|
| 6235 | index 000000000..f486651b6
|
|---|
| 6236 | --- /dev/null
|
|---|
| 6237 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 6238 | @@ -0,0 +1,354 @@
|
|---|
| 6239 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 6240 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 6241 | +
|
|---|
| 6242 | +import java.awt.geom.Area;
|
|---|
| 6243 | +import java.awt.geom.Ellipse2D;
|
|---|
| 6244 | +import java.awt.geom.Path2D;
|
|---|
| 6245 | +import java.awt.geom.PathIterator;
|
|---|
| 6246 | +import java.util.ArrayList;
|
|---|
| 6247 | +import java.util.Collection;
|
|---|
| 6248 | +import java.util.Collections;
|
|---|
| 6249 | +import java.util.List;
|
|---|
| 6250 | +import java.util.Objects;
|
|---|
| 6251 | +import java.util.Optional;
|
|---|
| 6252 | +import java.util.stream.Collectors;
|
|---|
| 6253 | +
|
|---|
| 6254 | +import org.openstreetmap.gui.jmapviewer.Coordinate;
|
|---|
| 6255 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 6256 | +import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
|---|
| 6257 | +import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 6258 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 6259 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 6260 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 6261 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 6262 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 6263 | +import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 6264 | +import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
|
|---|
| 6265 | +import org.openstreetmap.josm.data.osm.UniqueIdGenerator;
|
|---|
| 6266 | +import org.openstreetmap.josm.gui.dialogs.relation.sort.RelationSorter;
|
|---|
| 6267 | +import org.openstreetmap.josm.tools.Destroyable;
|
|---|
| 6268 | +import org.openstreetmap.josm.tools.Geometry;
|
|---|
| 6269 | +
|
|---|
| 6270 | +/**
|
|---|
| 6271 | + * A data store for Vector Data sets
|
|---|
| 6272 | + * @author Taylor Smock
|
|---|
| 6273 | + * @since xxx
|
|---|
| 6274 | + */
|
|---|
| 6275 | +class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay, VectorRelation> implements Destroyable {
|
|---|
| 6276 | + private static final String JOSM_MERGE_TYPE_KEY = "josm_merge_type";
|
|---|
| 6277 | + private final VectorDataSet dataSet;
|
|---|
| 6278 | +
|
|---|
| 6279 | + VectorDataStore(VectorDataSet dataSet, int zoom) {
|
|---|
| 6280 | + super(zoom);
|
|---|
| 6281 | + this.dataSet = dataSet;
|
|---|
| 6282 | + }
|
|---|
| 6283 | +
|
|---|
| 6284 | + @Override
|
|---|
| 6285 | + protected void addPrimitive(VectorPrimitive primitive) {
|
|---|
| 6286 | + primitive.setDataSet(this.dataSet);
|
|---|
| 6287 | + // The field is uint64, so we can use negative numbers to indicate that it is a "generated" object (e.g., nodes for ways)
|
|---|
| 6288 | + if (primitive.getUniqueId() == 0) {
|
|---|
| 6289 | + final UniqueIdGenerator generator = primitive.getIdGenerator();
|
|---|
| 6290 | + long id;
|
|---|
| 6291 | + do {
|
|---|
| 6292 | + id = generator.generateUniqueId();
|
|---|
| 6293 | + } while (this.primitivesMap.containsKey(new SimplePrimitiveId(id, primitive.getType())));
|
|---|
| 6294 | + primitive.setId(primitive.getIdGenerator().generateUniqueId());
|
|---|
| 6295 | + }
|
|---|
| 6296 | + if (primitive instanceof VectorRelation && !primitive.isMultipolygon()) {
|
|---|
| 6297 | + primitive = mergeWays((VectorRelation) primitive);
|
|---|
| 6298 | + }
|
|---|
| 6299 | + final VectorPrimitive alreadyAdded = this.primitivesMap.get(primitive.getPrimitiveId());
|
|---|
| 6300 | + final VectorRelation mergedRelation = (VectorRelation) this.primitivesMap
|
|---|
| 6301 | + .get(new SimplePrimitiveId(primitive.getPrimitiveId().getUniqueId(),
|
|---|
| 6302 | + OsmPrimitiveType.RELATION));
|
|---|
| 6303 | + if (alreadyAdded == null || alreadyAdded.equals(primitive)) {
|
|---|
| 6304 | + super.addPrimitive(primitive);
|
|---|
| 6305 | + } else if (mergedRelation != null && mergedRelation.get(JOSM_MERGE_TYPE_KEY) != null) {
|
|---|
| 6306 | + mergedRelation.addRelationMember(new VectorRelationMember("", primitive));
|
|---|
| 6307 | + super.addPrimitive(primitive);
|
|---|
| 6308 | + // Check that all primitives can be merged
|
|---|
| 6309 | + if (mergedRelation.getMemberPrimitivesList().stream().allMatch(IWay.class::isInstance)) {
|
|---|
| 6310 | + // This pretty much does the "right" thing
|
|---|
| 6311 | + this.mergeWays(mergedRelation);
|
|---|
| 6312 | + } else if (!(primitive instanceof IWay)) {
|
|---|
| 6313 | + // Can't merge, ever (one of the childs is a node/relation)
|
|---|
| 6314 | + mergedRelation.remove(JOSM_MERGE_TYPE_KEY);
|
|---|
| 6315 | + }
|
|---|
| 6316 | + } else if (mergedRelation != null && primitive instanceof IRelation) {
|
|---|
| 6317 | + // Just add to the relation
|
|---|
| 6318 | + ((VectorRelation) primitive).getMembers().forEach(mergedRelation::addRelationMember);
|
|---|
| 6319 | + } else if (alreadyAdded instanceof VectorWay && primitive instanceof VectorWay) {
|
|---|
| 6320 | + final VectorRelation temporaryRelation =
|
|---|
| 6321 | + mergedRelation == null ? new VectorRelation(primitive.getLayer()) : mergedRelation;
|
|---|
| 6322 | + if (mergedRelation == null) {
|
|---|
| 6323 | + temporaryRelation.put(JOSM_MERGE_TYPE_KEY, "merge");
|
|---|
| 6324 | + temporaryRelation.addRelationMember(new VectorRelationMember("", alreadyAdded));
|
|---|
| 6325 | + }
|
|---|
| 6326 | + temporaryRelation.addRelationMember(new VectorRelationMember("", primitive));
|
|---|
| 6327 | + temporaryRelation.setDataSet(this.dataSet);
|
|---|
| 6328 | + super.addPrimitive(primitive);
|
|---|
| 6329 | + super.addPrimitive(temporaryRelation);
|
|---|
| 6330 | + }
|
|---|
| 6331 | + }
|
|---|
| 6332 | +
|
|---|
| 6333 | + private VectorPrimitive mergeWays(VectorRelation relation) {
|
|---|
| 6334 | + List<VectorRelationMember> members = RelationSorter.sortMembersByConnectivity(relation.getMembers());
|
|---|
| 6335 | + Collection<VectorWay> relationWayList = members.stream().map(VectorRelationMember::getMember)
|
|---|
| 6336 | + .filter(VectorWay.class::isInstance)
|
|---|
| 6337 | + .map(VectorWay.class::cast).collect(Collectors.toCollection(ArrayList::new));
|
|---|
| 6338 | + // Only support way-only relations
|
|---|
| 6339 | + if (relationWayList.size() != relation.getMemberPrimitivesList().size()) {
|
|---|
| 6340 | + return relation;
|
|---|
| 6341 | + }
|
|---|
| 6342 | + List<VectorWay> wayList = new ArrayList<>(relation.getMembersCount());
|
|---|
| 6343 | + // Assume that the order may not be correct, worst case O(n), best case O(n/2)
|
|---|
| 6344 | + // Assume that the ways were drawn in order
|
|---|
| 6345 | + final int maxIteration = relationWayList.size();
|
|---|
| 6346 | + int iteration = 0;
|
|---|
| 6347 | + while (iteration < maxIteration && wayList.size() < relationWayList.size()) {
|
|---|
| 6348 | + for (VectorWay way : relationWayList) {
|
|---|
| 6349 | + if (wayList.isEmpty()) {
|
|---|
| 6350 | + wayList.add(way);
|
|---|
| 6351 | + continue;
|
|---|
| 6352 | + }
|
|---|
| 6353 | + // Check first/last ways (last first, since the list *should* be sorted)
|
|---|
| 6354 | + if (canMergeWays(wayList.get(wayList.size() - 1), way, false)) {
|
|---|
| 6355 | + wayList.add(way);
|
|---|
| 6356 | + } else if (canMergeWays(wayList.get(0), way, false)) {
|
|---|
| 6357 | + wayList.add(0, way);
|
|---|
| 6358 | + }
|
|---|
| 6359 | + }
|
|---|
| 6360 | + iteration++;
|
|---|
| 6361 | + relationWayList.removeIf(wayList::contains);
|
|---|
| 6362 | + }
|
|---|
| 6363 | + if (!relationWayList.isEmpty()) {
|
|---|
| 6364 | + return relation;
|
|---|
| 6365 | + }
|
|---|
| 6366 | + // Merge ways
|
|---|
| 6367 | + List<VectorNode> nodes = new ArrayList<>();
|
|---|
| 6368 | + for (VectorWay way : wayList) {
|
|---|
| 6369 | + for (VectorNode node : way.getNodes()) {
|
|---|
| 6370 | + if (nodes.isEmpty() || !Objects.equals(nodes.get(nodes.size() - 1), node)) {
|
|---|
| 6371 | + nodes.add(node);
|
|---|
| 6372 | + }
|
|---|
| 6373 | + }
|
|---|
| 6374 | + }
|
|---|
| 6375 | + VectorWay way = wayList.get(0);
|
|---|
| 6376 | + way.setNodes(nodes);
|
|---|
| 6377 | + wayList.remove(way);
|
|---|
| 6378 | + wayList.forEach(this::removePrimitive);
|
|---|
| 6379 | + this.removePrimitive(relation);
|
|---|
| 6380 | + return way;
|
|---|
| 6381 | + }
|
|---|
| 6382 | +
|
|---|
| 6383 | + private static <N extends INode, W extends IWay<N>> boolean canMergeWays(W old, W toAdd, boolean allowReverse) {
|
|---|
| 6384 | + final List<N> nodes = new ArrayList<>(old.getNodes());
|
|---|
| 6385 | + boolean added = true;
|
|---|
| 6386 | + if (allowReverse && old.firstNode().equals(toAdd.firstNode())) {
|
|---|
| 6387 | + // old <-|-> new becomes old ->|-> new
|
|---|
| 6388 | + Collections.reverse(nodes);
|
|---|
| 6389 | + nodes.addAll(toAdd.getNodes());
|
|---|
| 6390 | + } else if (old.firstNode().equals(toAdd.lastNode())) {
|
|---|
| 6391 | + // old <-|<- new, so we prepend the new nodes in order
|
|---|
| 6392 | + nodes.addAll(0, toAdd.getNodes());
|
|---|
| 6393 | + } else if (old.lastNode().equals(toAdd.firstNode())) {
|
|---|
| 6394 | + // old ->|-> new, we just add it
|
|---|
| 6395 | + nodes.addAll(toAdd.getNodes());
|
|---|
| 6396 | + } else if (allowReverse && old.lastNode().equals(toAdd.lastNode())) {
|
|---|
| 6397 | + // old ->|<- new, we need to reverse new
|
|---|
| 6398 | + final List<N> toAddNodes = new ArrayList<>(toAdd.getNodes());
|
|---|
| 6399 | + Collections.reverse(toAddNodes);
|
|---|
| 6400 | + nodes.addAll(toAddNodes);
|
|---|
| 6401 | + } else {
|
|---|
| 6402 | + added = false;
|
|---|
| 6403 | + }
|
|---|
| 6404 | + if (added) {
|
|---|
| 6405 | + // This is (technically) always correct
|
|---|
| 6406 | + old.setNodes(nodes);
|
|---|
| 6407 | + }
|
|---|
| 6408 | + return added;
|
|---|
| 6409 | + }
|
|---|
| 6410 | +
|
|---|
| 6411 | + private synchronized <T extends Tile & VectorTile> VectorNode pointToNode(T tile, Layer layer,
|
|---|
| 6412 | + Collection<VectorPrimitive> featureObjects, int x, int y) {
|
|---|
| 6413 | + final ICoordinate upperLeft = tile.getTileSource().tileXYToLatLon(tile);
|
|---|
| 6414 | + final int layerExtent = layer.getExtent() * 2;
|
|---|
| 6415 | + final ICoordinate lowerRight = tile.getTileSource()
|
|---|
| 6416 | + .tileXYToLatLon(tile.getXtile() + 1, tile.getYtile() + 1, tile.getZoom());
|
|---|
| 6417 | + final ICoordinate coords = new Coordinate(
|
|---|
| 6418 | + upperLeft.getLat() - (upperLeft.getLat() - lowerRight.getLat()) * y / layerExtent,
|
|---|
| 6419 | + upperLeft.getLon() + (lowerRight.getLon() - upperLeft.getLon()) * x / layerExtent);
|
|---|
| 6420 | + final Collection<VectorNode> nodes = this.store
|
|---|
| 6421 | + .searchNodes(new BBox(coords.getLon(), coords.getLat(), VectorDataSet.DUPE_NODE_DISTANCE));
|
|---|
| 6422 | + final VectorNode node;
|
|---|
| 6423 | + if (!nodes.isEmpty()) {
|
|---|
| 6424 | + final VectorNode first = nodes.iterator().next();
|
|---|
| 6425 | + if (first.isDisabled() || !first.isVisible()) {
|
|---|
| 6426 | + // Only replace nodes that are not visible
|
|---|
| 6427 | + node = new VectorNode(layer.getName());
|
|---|
| 6428 | + node.setCoor(node.getCoor());
|
|---|
| 6429 | + first.getReferrers(true).forEach(primitive -> {
|
|---|
| 6430 | + if (primitive instanceof VectorWay) {
|
|---|
| 6431 | + List<VectorNode> nodeList = new ArrayList<>(((VectorWay) primitive).getNodes());
|
|---|
| 6432 | + nodeList.replaceAll(vnode -> vnode.equals(first) ? node : vnode);
|
|---|
| 6433 | + ((VectorWay) primitive).setNodes(nodeList);
|
|---|
| 6434 | + } else if (primitive instanceof VectorRelation) {
|
|---|
| 6435 | + List<VectorRelationMember> members = new ArrayList<>(((VectorRelation) primitive).getMembers());
|
|---|
| 6436 | + members.replaceAll(member ->
|
|---|
| 6437 | + member.getMember().equals(first) ? new VectorRelationMember(member.getRole(), node) : member);
|
|---|
| 6438 | + ((VectorRelation) primitive).setMembers(members);
|
|---|
| 6439 | + }
|
|---|
| 6440 | + });
|
|---|
| 6441 | + this.removePrimitive(first);
|
|---|
| 6442 | + } else {
|
|---|
| 6443 | + node = first;
|
|---|
| 6444 | + }
|
|---|
| 6445 | + } else {
|
|---|
| 6446 | + node = new VectorNode(layer.getName());
|
|---|
| 6447 | + }
|
|---|
| 6448 | + node.setCoor(coords);
|
|---|
| 6449 | + featureObjects.add(node);
|
|---|
| 6450 | + return node;
|
|---|
| 6451 | + }
|
|---|
| 6452 | +
|
|---|
| 6453 | + private <T extends Tile & VectorTile> List<VectorWay> pathToWay(T tile, Layer layer,
|
|---|
| 6454 | + Collection<VectorPrimitive> featureObjects, Path2D shape) {
|
|---|
| 6455 | + final PathIterator pathIterator = shape.getPathIterator(null);
|
|---|
| 6456 | + final List<VectorWay> ways = pathIteratorToObjects(tile, layer, featureObjects, pathIterator).stream()
|
|---|
| 6457 | + .filter(VectorWay.class::isInstance).map(VectorWay.class::cast).collect(
|
|---|
| 6458 | + Collectors.toList());
|
|---|
| 6459 | + // These nodes technically do not exist, so we shouldn't show them
|
|---|
| 6460 | + ways.stream().flatMap(way -> way.getNodes().stream())
|
|---|
| 6461 | + .filter(prim -> !prim.isTagged() && prim.getReferrers(true).size() == 1 && prim.getId() <= 0)
|
|---|
| 6462 | + .forEach(prim -> {
|
|---|
| 6463 | + prim.setDisabled(true);
|
|---|
| 6464 | + prim.setVisible(false);
|
|---|
| 6465 | + });
|
|---|
| 6466 | + return ways;
|
|---|
| 6467 | + }
|
|---|
| 6468 | +
|
|---|
| 6469 | + private <T extends Tile & VectorTile> List<VectorPrimitive> pathIteratorToObjects(T tile, Layer layer,
|
|---|
| 6470 | + Collection<VectorPrimitive> featureObjects, PathIterator pathIterator) {
|
|---|
| 6471 | + final List<VectorNode> nodes = new ArrayList<>();
|
|---|
| 6472 | + final double[] coords = new double[6];
|
|---|
| 6473 | + final List<VectorPrimitive> ways = new ArrayList<>();
|
|---|
| 6474 | + do {
|
|---|
| 6475 | + final int type = pathIterator.currentSegment(coords);
|
|---|
| 6476 | + pathIterator.next();
|
|---|
| 6477 | + if ((PathIterator.SEG_MOVETO == type || PathIterator.SEG_CLOSE == type) && !nodes.isEmpty()) {
|
|---|
| 6478 | + if (PathIterator.SEG_CLOSE == type) {
|
|---|
| 6479 | + nodes.add(nodes.get(0));
|
|---|
| 6480 | + }
|
|---|
| 6481 | + // New line
|
|---|
| 6482 | + if (!nodes.isEmpty()) {
|
|---|
| 6483 | + final VectorWay way = new VectorWay(layer.getName());
|
|---|
| 6484 | + way.setNodes(nodes);
|
|---|
| 6485 | + featureObjects.add(way);
|
|---|
| 6486 | + ways.add(way);
|
|---|
| 6487 | + }
|
|---|
| 6488 | + nodes.clear();
|
|---|
| 6489 | + }
|
|---|
| 6490 | + if (PathIterator.SEG_MOVETO == type || PathIterator.SEG_LINETO == type) {
|
|---|
| 6491 | + final VectorNode node = pointToNode(tile, layer, featureObjects, (int) coords[0], (int) coords[1]);
|
|---|
| 6492 | + nodes.add(node);
|
|---|
| 6493 | + } else if (PathIterator.SEG_CLOSE != type) {
|
|---|
| 6494 | + // Vector Tiles only have MoveTo, LineTo, and ClosePath. Anything else is not supported at this time.
|
|---|
| 6495 | + throw new UnsupportedOperationException();
|
|---|
| 6496 | + }
|
|---|
| 6497 | + } while (!pathIterator.isDone());
|
|---|
| 6498 | + if (!nodes.isEmpty()) {
|
|---|
| 6499 | + final VectorWay way = new VectorWay(layer.getName());
|
|---|
| 6500 | + way.setNodes(nodes);
|
|---|
| 6501 | + featureObjects.add(way);
|
|---|
| 6502 | + ways.add(way);
|
|---|
| 6503 | + }
|
|---|
| 6504 | + return ways;
|
|---|
| 6505 | + }
|
|---|
| 6506 | +
|
|---|
| 6507 | + private <T extends Tile & VectorTile> VectorRelation areaToRelation(T tile, Layer layer,
|
|---|
| 6508 | + Collection<VectorPrimitive> featureObjects, Area area) {
|
|---|
| 6509 | + final PathIterator pathIterator = area.getPathIterator(null);
|
|---|
| 6510 | + final List<VectorPrimitive> members = pathIteratorToObjects(tile, layer, featureObjects, pathIterator);
|
|---|
| 6511 | + VectorRelation vectorRelation = new VectorRelation(layer.getName());
|
|---|
| 6512 | + for (VectorPrimitive member : members) {
|
|---|
| 6513 | + final String role;
|
|---|
| 6514 | + if (member instanceof VectorWay && ((VectorWay) member).isClosed()) {
|
|---|
| 6515 | + role = Geometry.isClockwise(((VectorWay) member).getNodes()) ? "outer" : "inner";
|
|---|
| 6516 | + } else {
|
|---|
| 6517 | + role = "";
|
|---|
| 6518 | + }
|
|---|
| 6519 | + vectorRelation.addRelationMember(new VectorRelationMember(role, member));
|
|---|
| 6520 | + }
|
|---|
| 6521 | + return vectorRelation;
|
|---|
| 6522 | + }
|
|---|
| 6523 | +
|
|---|
| 6524 | + /**
|
|---|
| 6525 | + * Add a tile to this data store
|
|---|
| 6526 | + * @param tile The tile to add
|
|---|
| 6527 | + * @param <T> The tile type
|
|---|
| 6528 | + */
|
|---|
| 6529 | + public synchronized <T extends Tile & VectorTile> void addTile(T tile) {
|
|---|
| 6530 | + Optional<Tile> previous = this.addedTiles.stream()
|
|---|
| 6531 | + .filter(t -> t.getTileXY().equals(tile.getTileXY()) && t.getZoom() == tile.getZoom()).findAny();
|
|---|
| 6532 | + // Check if we have already added the tile (just to save processing time)
|
|---|
| 6533 | + if (!previous.isPresent() || (!previous.get().isLoaded() && !previous.get().isLoading())) {
|
|---|
| 6534 | + previous.ifPresent(this.addedTiles::remove);
|
|---|
| 6535 | + this.addedTiles.add(tile);
|
|---|
| 6536 | + for (Layer layer : tile.getLayers()) {
|
|---|
| 6537 | + layer.getFeatures().forEach(feature -> {
|
|---|
| 6538 | + org.openstreetmap.josm.data.imagery.vectortile.mapbox.Geometry geometry = feature
|
|---|
| 6539 | + .getGeometryObject();
|
|---|
| 6540 | + List<VectorPrimitive> featureObjects = new ArrayList<>();
|
|---|
| 6541 | + List<VectorPrimitive> primaryFeatureObjects = new ArrayList<>();
|
|---|
| 6542 | + geometry.getShapes().forEach(shape -> {
|
|---|
| 6543 | + final VectorPrimitive primitive;
|
|---|
| 6544 | + if (shape instanceof Ellipse2D) {
|
|---|
| 6545 | + primitive = pointToNode(tile, layer, featureObjects,
|
|---|
| 6546 | + (int) ((Ellipse2D) shape).getCenterX(), (int) ((Ellipse2D) shape).getCenterY());
|
|---|
| 6547 | + } else if (shape instanceof Path2D) {
|
|---|
| 6548 | + primitive = pathToWay(tile, layer, featureObjects, (Path2D) shape).stream().findFirst()
|
|---|
| 6549 | + .orElse(null);
|
|---|
| 6550 | + } else if (shape instanceof Area) {
|
|---|
| 6551 | + primitive = areaToRelation(tile, layer, featureObjects, (Area) shape);
|
|---|
| 6552 | + primitive.put("type", "multipolygon");
|
|---|
| 6553 | + } else {
|
|---|
| 6554 | + // We shouldn't hit this, but just in case
|
|---|
| 6555 | + throw new UnsupportedOperationException();
|
|---|
| 6556 | + }
|
|---|
| 6557 | + primaryFeatureObjects.add(primitive);
|
|---|
| 6558 | + });
|
|---|
| 6559 | + final VectorPrimitive primitive;
|
|---|
| 6560 | + if (primaryFeatureObjects.size() == 1) {
|
|---|
| 6561 | + primitive = primaryFeatureObjects.get(0);
|
|---|
| 6562 | + if (primitive instanceof IRelation && !primitive.isMultipolygon()) {
|
|---|
| 6563 | + primitive.put(JOSM_MERGE_TYPE_KEY, "merge");
|
|---|
| 6564 | + }
|
|---|
| 6565 | + } else if (!primaryFeatureObjects.isEmpty()) {
|
|---|
| 6566 | + VectorRelation relation = new VectorRelation(layer.getName());
|
|---|
| 6567 | + primaryFeatureObjects.stream().map(prim -> new VectorRelationMember("", prim))
|
|---|
| 6568 | + .forEach(relation::addRelationMember);
|
|---|
| 6569 | + primitive = relation;
|
|---|
| 6570 | + } else {
|
|---|
| 6571 | + return;
|
|---|
| 6572 | + }
|
|---|
| 6573 | + primitive.setId(feature.getId());
|
|---|
| 6574 | + feature.getTags().forEach(primitive::put);
|
|---|
| 6575 | + featureObjects.forEach(this::addPrimitive);
|
|---|
| 6576 | + primaryFeatureObjects.forEach(this::addPrimitive);
|
|---|
| 6577 | + this.addPrimitive(primitive);
|
|---|
| 6578 | + });
|
|---|
| 6579 | + }
|
|---|
| 6580 | + }
|
|---|
| 6581 | + }
|
|---|
| 6582 | +
|
|---|
| 6583 | + @Override
|
|---|
| 6584 | + public void destroy() {
|
|---|
| 6585 | + this.addedTiles.forEach(tile -> tile.setLoaded(false));
|
|---|
| 6586 | + this.addedTiles.forEach(tile -> tile.setImage(null));
|
|---|
| 6587 | + this.addedTiles.clear();
|
|---|
| 6588 | + this.store.clear();
|
|---|
| 6589 | + this.allPrimitives.clear();
|
|---|
| 6590 | + this.primitivesMap.clear();
|
|---|
| 6591 | + }
|
|---|
| 6592 | +}
|
|---|
| 6593 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorNode.java b/src/org/openstreetmap/josm/data/vector/VectorNode.java
|
|---|
| 6594 | new file mode 100644
|
|---|
| 6595 | index 000000000..60aecd8ff
|
|---|
| 6596 | --- /dev/null
|
|---|
| 6597 | +++ b/src/org/openstreetmap/josm/data/vector/VectorNode.java
|
|---|
| 6598 | @@ -0,0 +1,113 @@
|
|---|
| 6599 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 6600 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 6601 | +
|
|---|
| 6602 | +import java.util.List;
|
|---|
| 6603 | +
|
|---|
| 6604 | +import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
|---|
| 6605 | +import org.openstreetmap.josm.data.coor.EastNorth;
|
|---|
| 6606 | +import org.openstreetmap.josm.data.coor.LatLon;
|
|---|
| 6607 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 6608 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 6609 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 6610 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 6611 | +import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 6612 | +import org.openstreetmap.josm.data.osm.UniqueIdGenerator;
|
|---|
| 6613 | +import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
|
|---|
| 6614 | +import org.openstreetmap.josm.data.projection.ProjectionRegistry;
|
|---|
| 6615 | +
|
|---|
| 6616 | +/**
|
|---|
| 6617 | + * The "Node" type of a vector layer
|
|---|
| 6618 | + *
|
|---|
| 6619 | + * @since xxx
|
|---|
| 6620 | + */
|
|---|
| 6621 | +public class VectorNode extends VectorPrimitive implements INode {
|
|---|
| 6622 | + private static final UniqueIdGenerator ID_GENERATOR = new UniqueIdGenerator();
|
|---|
| 6623 | + private double lon = Double.NaN;
|
|---|
| 6624 | + private double lat = Double.NaN;
|
|---|
| 6625 | +
|
|---|
| 6626 | + /**
|
|---|
| 6627 | + * Create a new vector node
|
|---|
| 6628 | + * @param layer The layer for the vector node
|
|---|
| 6629 | + */
|
|---|
| 6630 | + public VectorNode(String layer) {
|
|---|
| 6631 | + super(layer);
|
|---|
| 6632 | + }
|
|---|
| 6633 | +
|
|---|
| 6634 | + @Override
|
|---|
| 6635 | + public double lon() {
|
|---|
| 6636 | + return this.lon;
|
|---|
| 6637 | + }
|
|---|
| 6638 | +
|
|---|
| 6639 | + @Override
|
|---|
| 6640 | + public double lat() {
|
|---|
| 6641 | + return this.lat;
|
|---|
| 6642 | + }
|
|---|
| 6643 | +
|
|---|
| 6644 | + @Override
|
|---|
| 6645 | + public UniqueIdGenerator getIdGenerator() {
|
|---|
| 6646 | + return ID_GENERATOR;
|
|---|
| 6647 | + }
|
|---|
| 6648 | +
|
|---|
| 6649 | + @Override
|
|---|
| 6650 | + public LatLon getCoor() {
|
|---|
| 6651 | + return new LatLon(this.lat, this.lon);
|
|---|
| 6652 | + }
|
|---|
| 6653 | +
|
|---|
| 6654 | + @Override
|
|---|
| 6655 | + public void setCoor(LatLon coordinates) {
|
|---|
| 6656 | + this.lat = coordinates.lat();
|
|---|
| 6657 | + this.lon = coordinates.lon();
|
|---|
| 6658 | + }
|
|---|
| 6659 | +
|
|---|
| 6660 | + /**
|
|---|
| 6661 | + * Set the coordinates of this node
|
|---|
| 6662 | + *
|
|---|
| 6663 | + * @param coordinates The coordinates to set
|
|---|
| 6664 | + * @see #setCoor(LatLon)
|
|---|
| 6665 | + */
|
|---|
| 6666 | + public void setCoor(ICoordinate coordinates) {
|
|---|
| 6667 | + this.lat = coordinates.getLat();
|
|---|
| 6668 | + this.lon = coordinates.getLon();
|
|---|
| 6669 | + }
|
|---|
| 6670 | +
|
|---|
| 6671 | + @Override
|
|---|
| 6672 | + public void setEastNorth(EastNorth eastNorth) {
|
|---|
| 6673 | + final LatLon ll = ProjectionRegistry.getProjection().eastNorth2latlon(eastNorth);
|
|---|
| 6674 | + this.lat = ll.lat();
|
|---|
| 6675 | + this.lon = ll.lon();
|
|---|
| 6676 | + }
|
|---|
| 6677 | +
|
|---|
| 6678 | + @Override
|
|---|
| 6679 | + public boolean isReferredByWays(int n) {
|
|---|
| 6680 | + // Count only referrers that are members of the same dataset (primitive can have some fake references, for example
|
|---|
| 6681 | + // when way is cloned
|
|---|
| 6682 | + List<? extends IPrimitive> referrers = super.getReferrers();
|
|---|
| 6683 | + if (referrers == null || referrers.isEmpty())
|
|---|
| 6684 | + return false;
|
|---|
| 6685 | + if (referrers instanceof IPrimitive)
|
|---|
| 6686 | + return n <= 1 && referrers instanceof IWay && ((IPrimitive) referrers).getDataSet() == getDataSet();
|
|---|
| 6687 | + else {
|
|---|
| 6688 | + int counter = 0;
|
|---|
| 6689 | + for (IPrimitive o : referrers) {
|
|---|
| 6690 | + if (getDataSet() == o.getDataSet() && o instanceof IWay && ++counter >= n)
|
|---|
| 6691 | + return true;
|
|---|
| 6692 | + }
|
|---|
| 6693 | + return false;
|
|---|
| 6694 | + }
|
|---|
| 6695 | + }
|
|---|
| 6696 | +
|
|---|
| 6697 | + @Override
|
|---|
| 6698 | + public void accept(PrimitiveVisitor visitor) {
|
|---|
| 6699 | + visitor.visit(this);
|
|---|
| 6700 | + }
|
|---|
| 6701 | +
|
|---|
| 6702 | + @Override
|
|---|
| 6703 | + public BBox getBBox() {
|
|---|
| 6704 | + return new BBox(this.lon, this.lat);
|
|---|
| 6705 | + }
|
|---|
| 6706 | +
|
|---|
| 6707 | + @Override
|
|---|
| 6708 | + public OsmPrimitiveType getType() {
|
|---|
| 6709 | + return OsmPrimitiveType.NODE;
|
|---|
| 6710 | + }
|
|---|
| 6711 | +}
|
|---|
| 6712 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java b/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 6713 | new file mode 100644
|
|---|
| 6714 | index 000000000..17b5bef6f
|
|---|
| 6715 | --- /dev/null
|
|---|
| 6716 | +++ b/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 6717 | @@ -0,0 +1,256 @@
|
|---|
| 6718 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 6719 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 6720 | +
|
|---|
| 6721 | +import java.util.Arrays;
|
|---|
| 6722 | +import java.util.List;
|
|---|
| 6723 | +import java.util.Map;
|
|---|
| 6724 | +import java.util.function.Consumer;
|
|---|
| 6725 | +import java.util.stream.Collectors;
|
|---|
| 6726 | +import java.util.stream.IntStream;
|
|---|
| 6727 | +import java.util.stream.Stream;
|
|---|
| 6728 | +
|
|---|
| 6729 | +import org.openstreetmap.josm.data.osm.AbstractPrimitive;
|
|---|
| 6730 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 6731 | +import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
|
|---|
| 6732 | +import org.openstreetmap.josm.gui.mappaint.StyleCache;
|
|---|
| 6733 | +import org.openstreetmap.josm.tools.Utils;
|
|---|
| 6734 | +
|
|---|
| 6735 | +/**
|
|---|
| 6736 | + * The base class for Vector primitives
|
|---|
| 6737 | + * @author Taylor Smock
|
|---|
| 6738 | + * @since xxx
|
|---|
| 6739 | + */
|
|---|
| 6740 | +public abstract class VectorPrimitive extends AbstractPrimitive implements DataLayer<String> {
|
|---|
| 6741 | + private VectorDataSet dataSet;
|
|---|
| 6742 | + private boolean highlighted;
|
|---|
| 6743 | + private StyleCache mappaintStyle;
|
|---|
| 6744 | + private final String layer;
|
|---|
| 6745 | +
|
|---|
| 6746 | + /**
|
|---|
| 6747 | + * Create a primitive for a specific vector layer
|
|---|
| 6748 | + * @param layer The layer for the primitive
|
|---|
| 6749 | + */
|
|---|
| 6750 | + protected VectorPrimitive(String layer) {
|
|---|
| 6751 | + this.layer = layer;
|
|---|
| 6752 | + this.id = getIdGenerator().generateUniqueId();
|
|---|
| 6753 | + }
|
|---|
| 6754 | +
|
|---|
| 6755 | + @Override
|
|---|
| 6756 | + protected void keysChangedImpl(Map<String, String> originalKeys) {
|
|---|
| 6757 | + clearCachedStyle();
|
|---|
| 6758 | + if (dataSet != null) {
|
|---|
| 6759 | + for (IPrimitive ref : getReferrers()) {
|
|---|
| 6760 | + ref.clearCachedStyle();
|
|---|
| 6761 | + }
|
|---|
| 6762 | + }
|
|---|
| 6763 | + }
|
|---|
| 6764 | +
|
|---|
| 6765 | + @Override
|
|---|
| 6766 | + public boolean isHighlighted() {
|
|---|
| 6767 | + return this.highlighted;
|
|---|
| 6768 | + }
|
|---|
| 6769 | +
|
|---|
| 6770 | + @Override
|
|---|
| 6771 | + public void setHighlighted(boolean highlighted) {
|
|---|
| 6772 | + this.highlighted = highlighted;
|
|---|
| 6773 | + }
|
|---|
| 6774 | +
|
|---|
| 6775 | + @Override
|
|---|
| 6776 | + public boolean isTagged() {
|
|---|
| 6777 | + return !this.getInterestingTags().isEmpty();
|
|---|
| 6778 | + }
|
|---|
| 6779 | +
|
|---|
| 6780 | + @Override
|
|---|
| 6781 | + public boolean isAnnotated() {
|
|---|
| 6782 | + return this.getInterestingTags().size() - this.getKeys().size() > 0;
|
|---|
| 6783 | + }
|
|---|
| 6784 | +
|
|---|
| 6785 | + @Override
|
|---|
| 6786 | + public VectorDataSet getDataSet() {
|
|---|
| 6787 | + return this.dataSet;
|
|---|
| 6788 | + }
|
|---|
| 6789 | +
|
|---|
| 6790 | + protected void setDataSet(VectorDataSet dataSet) {
|
|---|
| 6791 | + this.dataSet = dataSet;
|
|---|
| 6792 | + }
|
|---|
| 6793 | +
|
|---|
| 6794 | + /*----------
|
|---|
| 6795 | + * MAPPAINT
|
|---|
| 6796 | + *--------*/
|
|---|
| 6797 | + private short mappaintCacheIdx;
|
|---|
| 6798 | +
|
|---|
| 6799 | + @Override
|
|---|
| 6800 | + public final StyleCache getCachedStyle() {
|
|---|
| 6801 | + return mappaintStyle;
|
|---|
| 6802 | + }
|
|---|
| 6803 | +
|
|---|
| 6804 | + @Override
|
|---|
| 6805 | + public final void setCachedStyle(StyleCache mappaintStyle) {
|
|---|
| 6806 | + this.mappaintStyle = mappaintStyle;
|
|---|
| 6807 | + }
|
|---|
| 6808 | +
|
|---|
| 6809 | + @Override
|
|---|
| 6810 | + public final boolean isCachedStyleUpToDate() {
|
|---|
| 6811 | + return mappaintStyle != null && mappaintCacheIdx == dataSet.getMappaintCacheIndex();
|
|---|
| 6812 | + }
|
|---|
| 6813 | +
|
|---|
| 6814 | + @Override
|
|---|
| 6815 | + public final void declareCachedStyleUpToDate() {
|
|---|
| 6816 | + this.mappaintCacheIdx = dataSet.getMappaintCacheIndex();
|
|---|
| 6817 | + }
|
|---|
| 6818 | +
|
|---|
| 6819 | + @Override
|
|---|
| 6820 | + public boolean hasDirectionKeys() {
|
|---|
| 6821 | + return false;
|
|---|
| 6822 | + }
|
|---|
| 6823 | +
|
|---|
| 6824 | + @Override
|
|---|
| 6825 | + public boolean reversedDirection() {
|
|---|
| 6826 | + return false;
|
|---|
| 6827 | + }
|
|---|
| 6828 | +
|
|---|
| 6829 | + /*------------
|
|---|
| 6830 | + * Referrers
|
|---|
| 6831 | + ------------*/
|
|---|
| 6832 | + // Largely the same as OsmPrimitive, OsmPrimitive not modified at this time to avoid breaking binary compatibility
|
|---|
| 6833 | +
|
|---|
| 6834 | + private Object referrers;
|
|---|
| 6835 | +
|
|---|
| 6836 | + @Override
|
|---|
| 6837 | + public final List<VectorPrimitive> getReferrers(boolean allowWithoutDataset) {
|
|---|
| 6838 | + return referrers(allowWithoutDataset, VectorPrimitive.class)
|
|---|
| 6839 | + .collect(Collectors.toList());
|
|---|
| 6840 | + }
|
|---|
| 6841 | +
|
|---|
| 6842 | + /**
|
|---|
| 6843 | + * Add new referrer. If referrer is already included then no action is taken
|
|---|
| 6844 | + * @param referrer The referrer to add
|
|---|
| 6845 | + */
|
|---|
| 6846 | + protected void addReferrer(IPrimitive referrer) {
|
|---|
| 6847 | + if (referrers == null) {
|
|---|
| 6848 | + referrers = referrer;
|
|---|
| 6849 | + } else if (referrers instanceof IPrimitive) {
|
|---|
| 6850 | + if (referrers != referrer) {
|
|---|
| 6851 | + referrers = new IPrimitive[] {(IPrimitive) referrers, referrer};
|
|---|
| 6852 | + }
|
|---|
| 6853 | + } else {
|
|---|
| 6854 | + for (IPrimitive primitive:(IPrimitive[]) referrers) {
|
|---|
| 6855 | + if (primitive == referrer)
|
|---|
| 6856 | + return;
|
|---|
| 6857 | + }
|
|---|
| 6858 | + referrers = Utils.addInArrayCopy((IPrimitive[]) referrers, referrer);
|
|---|
| 6859 | + }
|
|---|
| 6860 | + }
|
|---|
| 6861 | +
|
|---|
| 6862 | + /**
|
|---|
| 6863 | + * Remove referrer. No action is taken if referrer is not registered
|
|---|
| 6864 | + * @param referrer The referrer to remove
|
|---|
| 6865 | + */
|
|---|
| 6866 | + protected void removeReferrer(IPrimitive referrer) {
|
|---|
| 6867 | + if (referrers instanceof IPrimitive) {
|
|---|
| 6868 | + if (referrers == referrer) {
|
|---|
| 6869 | + referrers = null;
|
|---|
| 6870 | + }
|
|---|
| 6871 | + } else if (referrers instanceof IPrimitive[]) {
|
|---|
| 6872 | + IPrimitive[] orig = (IPrimitive[]) referrers;
|
|---|
| 6873 | + int idx = IntStream.range(0, orig.length)
|
|---|
| 6874 | + .filter(i -> orig[i] == referrer)
|
|---|
| 6875 | + .findFirst().orElse(-1);
|
|---|
| 6876 | + if (idx == -1)
|
|---|
| 6877 | + return;
|
|---|
| 6878 | +
|
|---|
| 6879 | + if (orig.length == 2) {
|
|---|
| 6880 | + referrers = orig[1-idx]; // idx is either 0 or 1, take the other
|
|---|
| 6881 | + } else { // downsize the array
|
|---|
| 6882 | + IPrimitive[] smaller = new IPrimitive[orig.length-1];
|
|---|
| 6883 | + System.arraycopy(orig, 0, smaller, 0, idx);
|
|---|
| 6884 | + System.arraycopy(orig, idx+1, smaller, idx, smaller.length-idx);
|
|---|
| 6885 | + referrers = smaller;
|
|---|
| 6886 | + }
|
|---|
| 6887 | + }
|
|---|
| 6888 | + }
|
|---|
| 6889 | +
|
|---|
| 6890 | + private <T extends IPrimitive> Stream<T> referrers(boolean allowWithoutDataset, Class<T> filter) {
|
|---|
| 6891 | + // Returns only referrers that are members of the same dataset (primitive can have some fake references, for example
|
|---|
| 6892 | + // when way is cloned
|
|---|
| 6893 | +
|
|---|
| 6894 | + if (dataSet == null && !allowWithoutDataset) {
|
|---|
| 6895 | + return Stream.empty();
|
|---|
| 6896 | + }
|
|---|
| 6897 | + if (referrers == null) {
|
|---|
| 6898 | + return Stream.empty();
|
|---|
| 6899 | + }
|
|---|
| 6900 | + final Stream<IPrimitive> stream = referrers instanceof IPrimitive // NOPMD
|
|---|
| 6901 | + ? Stream.of((IPrimitive) referrers)
|
|---|
| 6902 | + : Arrays.stream((IPrimitive[]) referrers);
|
|---|
| 6903 | + return stream
|
|---|
| 6904 | + .filter(p -> p.getDataSet() == dataSet)
|
|---|
| 6905 | + .filter(filter::isInstance)
|
|---|
| 6906 | + .map(filter::cast);
|
|---|
| 6907 | + }
|
|---|
| 6908 | +
|
|---|
| 6909 | + /**
|
|---|
| 6910 | + * Gets all primitives in the current dataset that reference this primitive.
|
|---|
| 6911 | + * @param filter restrict primitives to subclasses
|
|---|
| 6912 | + * @param <T> type of primitives
|
|---|
| 6913 | + * @return the referrers as Stream
|
|---|
| 6914 | + */
|
|---|
| 6915 | + public final <T extends IPrimitive> Stream<T> referrers(Class<T> filter) {
|
|---|
| 6916 | + return referrers(false, filter);
|
|---|
| 6917 | + }
|
|---|
| 6918 | +
|
|---|
| 6919 | + @Override
|
|---|
| 6920 | + public void visitReferrers(PrimitiveVisitor visitor) {
|
|---|
| 6921 | + if (visitor != null)
|
|---|
| 6922 | + doVisitReferrers(o -> o.accept(visitor));
|
|---|
| 6923 | + }
|
|---|
| 6924 | +
|
|---|
| 6925 | + private void doVisitReferrers(Consumer<IPrimitive> visitor) {
|
|---|
| 6926 | + if (this.referrers instanceof IPrimitive) {
|
|---|
| 6927 | + IPrimitive ref = (IPrimitive) this.referrers;
|
|---|
| 6928 | + if (ref.getDataSet() == dataSet) {
|
|---|
| 6929 | + visitor.accept(ref);
|
|---|
| 6930 | + }
|
|---|
| 6931 | + } else if (this.referrers instanceof IPrimitive[]) {
|
|---|
| 6932 | + IPrimitive[] refs = (IPrimitive[]) this.referrers;
|
|---|
| 6933 | + for (IPrimitive ref: refs) {
|
|---|
| 6934 | + if (ref.getDataSet() == dataSet) {
|
|---|
| 6935 | + visitor.accept(ref);
|
|---|
| 6936 | + }
|
|---|
| 6937 | + }
|
|---|
| 6938 | + }
|
|---|
| 6939 | + }
|
|---|
| 6940 | +
|
|---|
| 6941 | + /**
|
|---|
| 6942 | + * Set the id of the object
|
|---|
| 6943 | + * @param id The id
|
|---|
| 6944 | + */
|
|---|
| 6945 | + protected void setId(long id) {
|
|---|
| 6946 | + this.id = id;
|
|---|
| 6947 | + }
|
|---|
| 6948 | +
|
|---|
| 6949 | + /**
|
|---|
| 6950 | + * Make this object disabled
|
|---|
| 6951 | + * @param disabled {@code true} to disable the object
|
|---|
| 6952 | + */
|
|---|
| 6953 | + public void setDisabled(boolean disabled) {
|
|---|
| 6954 | + this.updateFlags(FLAG_DISABLED, disabled);
|
|---|
| 6955 | + }
|
|---|
| 6956 | +
|
|---|
| 6957 | + /**
|
|---|
| 6958 | + * Make this object visible
|
|---|
| 6959 | + * @param visible {@code true} to make this object visible (default)
|
|---|
| 6960 | + */
|
|---|
| 6961 | + @Override
|
|---|
| 6962 | + public void setVisible(boolean visible) {
|
|---|
| 6963 | + this.updateFlags(FLAG_VISIBLE, visible);
|
|---|
| 6964 | + }
|
|---|
| 6965 | +
|
|---|
| 6966 | + /**************************
|
|---|
| 6967 | + * Data layer information *
|
|---|
| 6968 | + **************************/
|
|---|
| 6969 | + @Override
|
|---|
| 6970 | + public String getLayer() {
|
|---|
| 6971 | + return this.layer;
|
|---|
| 6972 | + }
|
|---|
| 6973 | +}
|
|---|
| 6974 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorRelation.java b/src/org/openstreetmap/josm/data/vector/VectorRelation.java
|
|---|
| 6975 | new file mode 100644
|
|---|
| 6976 | index 000000000..0deb57e57
|
|---|
| 6977 | --- /dev/null
|
|---|
| 6978 | +++ b/src/org/openstreetmap/josm/data/vector/VectorRelation.java
|
|---|
| 6979 | @@ -0,0 +1,114 @@
|
|---|
| 6980 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 6981 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 6982 | +
|
|---|
| 6983 | +import java.util.ArrayList;
|
|---|
| 6984 | +import java.util.Collections;
|
|---|
| 6985 | +import java.util.List;
|
|---|
| 6986 | +
|
|---|
| 6987 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 6988 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 6989 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 6990 | +import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 6991 | +import org.openstreetmap.josm.data.osm.UniqueIdGenerator;
|
|---|
| 6992 | +import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
|
|---|
| 6993 | +
|
|---|
| 6994 | +/**
|
|---|
| 6995 | + * The "Relation" type for vectors
|
|---|
| 6996 | + *
|
|---|
| 6997 | + * @author Taylor Smock
|
|---|
| 6998 | + * @since xxx
|
|---|
| 6999 | + */
|
|---|
| 7000 | +public class VectorRelation extends VectorPrimitive implements IRelation<VectorRelationMember> {
|
|---|
| 7001 | + private static final UniqueIdGenerator RELATION_ID_GENERATOR = new UniqueIdGenerator();
|
|---|
| 7002 | + private final List<VectorRelationMember> members = new ArrayList<>();
|
|---|
| 7003 | + private BBox cachedBBox;
|
|---|
| 7004 | +
|
|---|
| 7005 | + /**
|
|---|
| 7006 | + * Create a new relation for a layer
|
|---|
| 7007 | + * @param layer The layer the relation will belong to
|
|---|
| 7008 | + */
|
|---|
| 7009 | + public VectorRelation(String layer) {
|
|---|
| 7010 | + super(layer);
|
|---|
| 7011 | + }
|
|---|
| 7012 | +
|
|---|
| 7013 | + @Override
|
|---|
| 7014 | + public UniqueIdGenerator getIdGenerator() {
|
|---|
| 7015 | + return RELATION_ID_GENERATOR;
|
|---|
| 7016 | + }
|
|---|
| 7017 | +
|
|---|
| 7018 | + @Override
|
|---|
| 7019 | + public void accept(PrimitiveVisitor visitor) {
|
|---|
| 7020 | + visitor.visit(this);
|
|---|
| 7021 | + }
|
|---|
| 7022 | +
|
|---|
| 7023 | + @Override
|
|---|
| 7024 | + public BBox getBBox() {
|
|---|
| 7025 | + if (cachedBBox == null) {
|
|---|
| 7026 | + cachedBBox = new BBox();
|
|---|
| 7027 | + for (IPrimitive member : this.getMemberPrimitivesList()) {
|
|---|
| 7028 | + cachedBBox.add(member.getBBox());
|
|---|
| 7029 | + }
|
|---|
| 7030 | + }
|
|---|
| 7031 | + return cachedBBox;
|
|---|
| 7032 | + }
|
|---|
| 7033 | +
|
|---|
| 7034 | + protected void addRelationMember(VectorRelationMember member) {
|
|---|
| 7035 | + this.members.add(member);
|
|---|
| 7036 | + member.getMember().addReferrer(this);
|
|---|
| 7037 | + cachedBBox = null;
|
|---|
| 7038 | + }
|
|---|
| 7039 | +
|
|---|
| 7040 | + /**
|
|---|
| 7041 | + * Remove the first instance of a member from the relation
|
|---|
| 7042 | + *
|
|---|
| 7043 | + * @param member The member to remove
|
|---|
| 7044 | + */
|
|---|
| 7045 | + protected void removeRelationMember(VectorRelationMember member) {
|
|---|
| 7046 | + this.members.remove(member);
|
|---|
| 7047 | + if (!this.members.contains(member)) {
|
|---|
| 7048 | + member.getMember().removeReferrer(this);
|
|---|
| 7049 | + }
|
|---|
| 7050 | + }
|
|---|
| 7051 | +
|
|---|
| 7052 | + @Override
|
|---|
| 7053 | + public int getMembersCount() {
|
|---|
| 7054 | + return this.members.size();
|
|---|
| 7055 | + }
|
|---|
| 7056 | +
|
|---|
| 7057 | + @Override
|
|---|
| 7058 | + public VectorRelationMember getMember(int index) {
|
|---|
| 7059 | + return this.members.get(index);
|
|---|
| 7060 | + }
|
|---|
| 7061 | +
|
|---|
| 7062 | + @Override
|
|---|
| 7063 | + public List<VectorRelationMember> getMembers() {
|
|---|
| 7064 | + return Collections.unmodifiableList(this.members);
|
|---|
| 7065 | + }
|
|---|
| 7066 | +
|
|---|
| 7067 | + @Override
|
|---|
| 7068 | + public void setMembers(List<VectorRelationMember> members) {
|
|---|
| 7069 | + this.members.clear();
|
|---|
| 7070 | + this.members.addAll(members);
|
|---|
| 7071 | + }
|
|---|
| 7072 | +
|
|---|
| 7073 | + @Override
|
|---|
| 7074 | + public long getMemberId(int idx) {
|
|---|
| 7075 | + return this.getMember(idx).getMember().getId();
|
|---|
| 7076 | + }
|
|---|
| 7077 | +
|
|---|
| 7078 | + @Override
|
|---|
| 7079 | + public String getRole(int idx) {
|
|---|
| 7080 | + return this.getMember(idx).getRole();
|
|---|
| 7081 | + }
|
|---|
| 7082 | +
|
|---|
| 7083 | + @Override
|
|---|
| 7084 | + public OsmPrimitiveType getMemberType(int idx) {
|
|---|
| 7085 | + return this.getMember(idx).getType();
|
|---|
| 7086 | + }
|
|---|
| 7087 | +
|
|---|
| 7088 | + @Override
|
|---|
| 7089 | + public OsmPrimitiveType getType() {
|
|---|
| 7090 | + return this.getMembers().stream().map(VectorRelationMember::getType)
|
|---|
| 7091 | + .allMatch(OsmPrimitiveType.CLOSEDWAY::equals) ? OsmPrimitiveType.MULTIPOLYGON : OsmPrimitiveType.RELATION;
|
|---|
| 7092 | + }
|
|---|
| 7093 | +}
|
|---|
| 7094 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorRelationMember.java b/src/org/openstreetmap/josm/data/vector/VectorRelationMember.java
|
|---|
| 7095 | new file mode 100644
|
|---|
| 7096 | index 000000000..56d6dfe77
|
|---|
| 7097 | --- /dev/null
|
|---|
| 7098 | +++ b/src/org/openstreetmap/josm/data/vector/VectorRelationMember.java
|
|---|
| 7099 | @@ -0,0 +1,70 @@
|
|---|
| 7100 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 7101 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 7102 | +
|
|---|
| 7103 | +import java.util.Optional;
|
|---|
| 7104 | +
|
|---|
| 7105 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 7106 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 7107 | +import org.openstreetmap.josm.data.osm.IRelationMember;
|
|---|
| 7108 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 7109 | +import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 7110 | +import org.openstreetmap.josm.tools.CheckParameterUtil;
|
|---|
| 7111 | +
|
|---|
| 7112 | +/**
|
|---|
| 7113 | + * Relation members for a Vector Relation
|
|---|
| 7114 | + */
|
|---|
| 7115 | +public class VectorRelationMember implements IRelationMember<VectorPrimitive> {
|
|---|
| 7116 | + private final String role;
|
|---|
| 7117 | + private final VectorPrimitive member;
|
|---|
| 7118 | +
|
|---|
| 7119 | + /**
|
|---|
| 7120 | + * Create a new relation member
|
|---|
| 7121 | + * @param role The role of the member
|
|---|
| 7122 | + * @param member The member primitive
|
|---|
| 7123 | + */
|
|---|
| 7124 | + public VectorRelationMember(String role, VectorPrimitive member) {
|
|---|
| 7125 | + CheckParameterUtil.ensureParameterNotNull(member, "member");
|
|---|
| 7126 | + this.role = Optional.ofNullable(role).orElse("").intern();
|
|---|
| 7127 | + this.member = member;
|
|---|
| 7128 | + }
|
|---|
| 7129 | +
|
|---|
| 7130 | + @Override
|
|---|
| 7131 | + public String getRole() {
|
|---|
| 7132 | + return this.role;
|
|---|
| 7133 | + }
|
|---|
| 7134 | +
|
|---|
| 7135 | + @Override
|
|---|
| 7136 | + public boolean isNode() {
|
|---|
| 7137 | + return this.member instanceof INode;
|
|---|
| 7138 | + }
|
|---|
| 7139 | +
|
|---|
| 7140 | + @Override
|
|---|
| 7141 | + public boolean isWay() {
|
|---|
| 7142 | + return this.member instanceof IWay;
|
|---|
| 7143 | + }
|
|---|
| 7144 | +
|
|---|
| 7145 | + @Override
|
|---|
| 7146 | + public boolean isRelation() {
|
|---|
| 7147 | + return this.member instanceof IRelation;
|
|---|
| 7148 | + }
|
|---|
| 7149 | +
|
|---|
| 7150 | + @Override
|
|---|
| 7151 | + public VectorPrimitive getMember() {
|
|---|
| 7152 | + return this.member;
|
|---|
| 7153 | + }
|
|---|
| 7154 | +
|
|---|
| 7155 | + @Override
|
|---|
| 7156 | + public long getUniqueId() {
|
|---|
| 7157 | + return this.member.getId();
|
|---|
| 7158 | + }
|
|---|
| 7159 | +
|
|---|
| 7160 | + @Override
|
|---|
| 7161 | + public OsmPrimitiveType getType() {
|
|---|
| 7162 | + return this.member.getType();
|
|---|
| 7163 | + }
|
|---|
| 7164 | +
|
|---|
| 7165 | + @Override
|
|---|
| 7166 | + public boolean isNew() {
|
|---|
| 7167 | + return this.member.isNew();
|
|---|
| 7168 | + }
|
|---|
| 7169 | +}
|
|---|
| 7170 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorWay.java b/src/org/openstreetmap/josm/data/vector/VectorWay.java
|
|---|
| 7171 | new file mode 100644
|
|---|
| 7172 | index 000000000..582fca2d4
|
|---|
| 7173 | --- /dev/null
|
|---|
| 7174 | +++ b/src/org/openstreetmap/josm/data/vector/VectorWay.java
|
|---|
| 7175 | @@ -0,0 +1,132 @@
|
|---|
| 7176 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 7177 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 7178 | +
|
|---|
| 7179 | +import java.util.ArrayList;
|
|---|
| 7180 | +import java.util.Collections;
|
|---|
| 7181 | +import java.util.List;
|
|---|
| 7182 | +import java.util.stream.Collectors;
|
|---|
| 7183 | +
|
|---|
| 7184 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 7185 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 7186 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 7187 | +import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 7188 | +import org.openstreetmap.josm.data.osm.UniqueIdGenerator;
|
|---|
| 7189 | +import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
|
|---|
| 7190 | +
|
|---|
| 7191 | +/**
|
|---|
| 7192 | + * The "Way" type for a Vector layer
|
|---|
| 7193 | + *
|
|---|
| 7194 | + * @author Taylor Smock
|
|---|
| 7195 | + * @since xxx
|
|---|
| 7196 | + */
|
|---|
| 7197 | +public class VectorWay extends VectorPrimitive implements IWay<VectorNode> {
|
|---|
| 7198 | + private static final UniqueIdGenerator WAY_GENERATOR = new UniqueIdGenerator();
|
|---|
| 7199 | + private final List<VectorNode> nodes = new ArrayList<>();
|
|---|
| 7200 | + private BBox cachedBBox;
|
|---|
| 7201 | +
|
|---|
| 7202 | + /**
|
|---|
| 7203 | + * Create a new way for a layer
|
|---|
| 7204 | + * @param layer The layer for the way
|
|---|
| 7205 | + */
|
|---|
| 7206 | + public VectorWay(String layer) {
|
|---|
| 7207 | + super(layer);
|
|---|
| 7208 | + }
|
|---|
| 7209 | +
|
|---|
| 7210 | + @Override
|
|---|
| 7211 | + public UniqueIdGenerator getIdGenerator() {
|
|---|
| 7212 | + return WAY_GENERATOR;
|
|---|
| 7213 | + }
|
|---|
| 7214 | +
|
|---|
| 7215 | + @Override
|
|---|
| 7216 | + public void accept(PrimitiveVisitor visitor) {
|
|---|
| 7217 | + visitor.visit(this);
|
|---|
| 7218 | + }
|
|---|
| 7219 | +
|
|---|
| 7220 | + @Override
|
|---|
| 7221 | + public BBox getBBox() {
|
|---|
| 7222 | + if (cachedBBox == null) {
|
|---|
| 7223 | + cachedBBox = new BBox();
|
|---|
| 7224 | + for (INode node : this.getNodes()) {
|
|---|
| 7225 | + cachedBBox.add(node.getBBox());
|
|---|
| 7226 | + }
|
|---|
| 7227 | + }
|
|---|
| 7228 | + return cachedBBox;
|
|---|
| 7229 | + }
|
|---|
| 7230 | +
|
|---|
| 7231 | + @Override
|
|---|
| 7232 | + public int getNodesCount() {
|
|---|
| 7233 | + return this.getNodes().size();
|
|---|
| 7234 | + }
|
|---|
| 7235 | +
|
|---|
| 7236 | + @Override
|
|---|
| 7237 | + public VectorNode getNode(int index) {
|
|---|
| 7238 | + return this.getNodes().get(index);
|
|---|
| 7239 | + }
|
|---|
| 7240 | +
|
|---|
| 7241 | + @Override
|
|---|
| 7242 | + public List<VectorNode> getNodes() {
|
|---|
| 7243 | + return Collections.unmodifiableList(this.nodes);
|
|---|
| 7244 | + }
|
|---|
| 7245 | +
|
|---|
| 7246 | + @Override
|
|---|
| 7247 | + public void setNodes(List<VectorNode> nodes) {
|
|---|
| 7248 | + this.nodes.forEach(node -> node.removeReferrer(this));
|
|---|
| 7249 | + this.nodes.clear();
|
|---|
| 7250 | + nodes.forEach(node -> node.addReferrer(this));
|
|---|
| 7251 | + this.nodes.addAll(nodes);
|
|---|
| 7252 | + this.cachedBBox = null;
|
|---|
| 7253 | + }
|
|---|
| 7254 | +
|
|---|
| 7255 | + @Override
|
|---|
| 7256 | + public List<Long> getNodeIds() {
|
|---|
| 7257 | + return this.getNodes().stream().map(VectorNode::getId).collect(Collectors.toList());
|
|---|
| 7258 | + }
|
|---|
| 7259 | +
|
|---|
| 7260 | + @Override
|
|---|
| 7261 | + public long getNodeId(int idx) {
|
|---|
| 7262 | + return this.getNodes().get(idx).getId();
|
|---|
| 7263 | + }
|
|---|
| 7264 | +
|
|---|
| 7265 | + @Override
|
|---|
| 7266 | + public boolean isClosed() {
|
|---|
| 7267 | + return this.firstNode() != null && this.firstNode().equals(this.lastNode());
|
|---|
| 7268 | + }
|
|---|
| 7269 | +
|
|---|
| 7270 | + @Override
|
|---|
| 7271 | + public VectorNode firstNode() {
|
|---|
| 7272 | + if (this.nodes.isEmpty()) {
|
|---|
| 7273 | + return null;
|
|---|
| 7274 | + }
|
|---|
| 7275 | + return this.getNode(0);
|
|---|
| 7276 | + }
|
|---|
| 7277 | +
|
|---|
| 7278 | + @Override
|
|---|
| 7279 | + public VectorNode lastNode() {
|
|---|
| 7280 | + if (this.nodes.isEmpty()) {
|
|---|
| 7281 | + return null;
|
|---|
| 7282 | + }
|
|---|
| 7283 | + return this.getNode(this.getNodesCount() - 1);
|
|---|
| 7284 | + }
|
|---|
| 7285 | +
|
|---|
| 7286 | + @Override
|
|---|
| 7287 | + public boolean isFirstLastNode(INode n) {
|
|---|
| 7288 | + if (this.nodes.isEmpty()) {
|
|---|
| 7289 | + return false;
|
|---|
| 7290 | + }
|
|---|
| 7291 | + return this.firstNode().equals(n) || this.lastNode().equals(n);
|
|---|
| 7292 | + }
|
|---|
| 7293 | +
|
|---|
| 7294 | + @Override
|
|---|
| 7295 | + public boolean isInnerNode(INode n) {
|
|---|
| 7296 | + if (this.nodes.isEmpty()) {
|
|---|
| 7297 | + return false;
|
|---|
| 7298 | + }
|
|---|
| 7299 | + return !this.firstNode().equals(n) && !this.lastNode().equals(n) && this.nodes.stream()
|
|---|
| 7300 | + .anyMatch(vectorNode -> vectorNode.equals(n));
|
|---|
| 7301 | + }
|
|---|
| 7302 | +
|
|---|
| 7303 | + @Override
|
|---|
| 7304 | + public OsmPrimitiveType getType() {
|
|---|
| 7305 | + return this.isClosed() ? OsmPrimitiveType.CLOSEDWAY : OsmPrimitiveType.WAY;
|
|---|
| 7306 | + }
|
|---|
| 7307 | +}
|
|---|
| 7308 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 7309 | new file mode 100644
|
|---|
| 7310 | index 000000000..8983e8397
|
|---|
| 7311 | --- /dev/null
|
|---|
| 7312 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 7313 | @@ -0,0 +1,141 @@
|
|---|
| 7314 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 7315 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 7316 | +
|
|---|
| 7317 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 7318 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 7319 | +
|
|---|
| 7320 | +
|
|---|
| 7321 | +import java.nio.file.Paths;
|
|---|
| 7322 | +import java.text.MessageFormat;
|
|---|
| 7323 | +import java.util.ArrayList;
|
|---|
| 7324 | +import java.util.Collection;
|
|---|
| 7325 | +import java.util.Collections;
|
|---|
| 7326 | +import java.util.HashSet;
|
|---|
| 7327 | +import java.util.List;
|
|---|
| 7328 | +import java.util.Map;
|
|---|
| 7329 | +import java.util.stream.Collectors;
|
|---|
| 7330 | +
|
|---|
| 7331 | +import org.openstreetmap.josm.TestUtils;
|
|---|
| 7332 | +import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 7333 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 7334 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapBoxVectorCachedTileLoader;
|
|---|
| 7335 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorTileSource;
|
|---|
| 7336 | +import org.openstreetmap.josm.gui.layer.imagery.MVTLayer;
|
|---|
| 7337 | +import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 7338 | +
|
|---|
| 7339 | +import org.awaitility.Awaitility;
|
|---|
| 7340 | +import org.awaitility.Durations;
|
|---|
| 7341 | +import org.junit.jupiter.api.BeforeEach;
|
|---|
| 7342 | +import org.junit.jupiter.api.Test;
|
|---|
| 7343 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 7344 | +
|
|---|
| 7345 | +/**
|
|---|
| 7346 | + * A test for {@link VectorDataSet}
|
|---|
| 7347 | + */
|
|---|
| 7348 | +class VectorDataSetTest {
|
|---|
| 7349 | + /**
|
|---|
| 7350 | + * Make some methods available for this test class
|
|---|
| 7351 | + */
|
|---|
| 7352 | + private static class MVTLayerMock extends MVTLayer {
|
|---|
| 7353 | + private final Collection<MVTTile> finishedLoading = new HashSet<>();
|
|---|
| 7354 | +
|
|---|
| 7355 | + MVTLayerMock(ImageryInfo info) {
|
|---|
| 7356 | + super(info);
|
|---|
| 7357 | + }
|
|---|
| 7358 | +
|
|---|
| 7359 | + @Override
|
|---|
| 7360 | + protected MapboxVectorTileSource getTileSource() {
|
|---|
| 7361 | + return super.getTileSource();
|
|---|
| 7362 | + }
|
|---|
| 7363 | +
|
|---|
| 7364 | + protected MapBoxVectorCachedTileLoader getTileLoader() {
|
|---|
| 7365 | + if (this.tileLoader == null) {
|
|---|
| 7366 | + this.tileLoader = this.getTileLoaderFactory().makeTileLoader(this, Collections.emptyMap(), 7200);
|
|---|
| 7367 | + }
|
|---|
| 7368 | + if (this.tileLoader instanceof MapBoxVectorCachedTileLoader) {
|
|---|
| 7369 | + return (MapBoxVectorCachedTileLoader) this.tileLoader;
|
|---|
| 7370 | + }
|
|---|
| 7371 | + return null;
|
|---|
| 7372 | + }
|
|---|
| 7373 | +
|
|---|
| 7374 | + @Override
|
|---|
| 7375 | + public void finishedLoading(MVTTile tile) {
|
|---|
| 7376 | + super.finishedLoading(tile);
|
|---|
| 7377 | + this.finishedLoading.add(tile);
|
|---|
| 7378 | + }
|
|---|
| 7379 | +
|
|---|
| 7380 | + public Collection<MVTTile> finishedLoading() {
|
|---|
| 7381 | + return this.finishedLoading;
|
|---|
| 7382 | + }
|
|---|
| 7383 | + }
|
|---|
| 7384 | +
|
|---|
| 7385 | + @RegisterExtension
|
|---|
| 7386 | + JOSMTestRules rule = new JOSMTestRules().projection();
|
|---|
| 7387 | +
|
|---|
| 7388 | + /**
|
|---|
| 7389 | + * Load arbitrary tiles
|
|---|
| 7390 | + * @param layer The layer to add the tiles to
|
|---|
| 7391 | + * @param tiles The tiles to load ([z, x, y, z, x, y, ...]) -- must be divisible by three
|
|---|
| 7392 | + */
|
|---|
| 7393 | + private static void loadTile(MVTLayerMock layer, int... tiles) {
|
|---|
| 7394 | + if (tiles.length % 3 != 0 || tiles.length == 0) {
|
|---|
| 7395 | + throw new IllegalArgumentException("Tiles come with a {z}, {x}, and {y} component");
|
|---|
| 7396 | + }
|
|---|
| 7397 | + final MapboxVectorTileSource tileSource = layer.getTileSource();
|
|---|
| 7398 | + MapBoxVectorCachedTileLoader tileLoader = layer.getTileLoader();
|
|---|
| 7399 | + Collection<MVTTile> tilesCollection = new ArrayList<>();
|
|---|
| 7400 | + for (int i = 0; i < tiles.length / 3; i++) {
|
|---|
| 7401 | + final MVTTile tile = (MVTTile) layer.createTile(tileSource, tiles[3 * i + 1], tiles[3 * i + 2], tiles[3 * i]);
|
|---|
| 7402 | + tileLoader.createTileLoaderJob(tile).submit();
|
|---|
| 7403 | + tilesCollection.add(tile);
|
|---|
| 7404 | + }
|
|---|
| 7405 | + Awaitility.await().atMost(Durations.FIVE_SECONDS).until(() -> layer.finishedLoading().size() == tilesCollection
|
|---|
| 7406 | + .size());
|
|---|
| 7407 | + }
|
|---|
| 7408 | +
|
|---|
| 7409 | + private MVTLayerMock layer;
|
|---|
| 7410 | +
|
|---|
| 7411 | + @BeforeEach
|
|---|
| 7412 | + void setup() {
|
|---|
| 7413 | + // Create the preconditions for the test
|
|---|
| 7414 | + final ImageryInfo info = new ImageryInfo();
|
|---|
| 7415 | + info.setName("en", "Test info");
|
|---|
| 7416 | + info.setUrl("file:/" + Paths.get(TestUtils.getTestDataRoot(), "pbf", "mapillary", "{z}", "{x}", "{y}.mvt"));
|
|---|
| 7417 | + layer = new MVTLayerMock(info);
|
|---|
| 7418 | + }
|
|---|
| 7419 | +
|
|---|
| 7420 | + @Test
|
|---|
| 7421 | + void testNodeDeduplication() {
|
|---|
| 7422 | + final VectorDataSet dataSet = this.layer.getData();
|
|---|
| 7423 | + assertTrue(dataSet.allPrimitives().isEmpty());
|
|---|
| 7424 | +
|
|---|
| 7425 | + // Set the zoom to 14, as that is the tile we are checking
|
|---|
| 7426 | + dataSet.setZoom(14);
|
|---|
| 7427 | + loadTile(this.layer, 14, 3248, 6258);
|
|---|
| 7428 | +
|
|---|
| 7429 | + // There _does_ appear to be some kind of race condition though
|
|---|
| 7430 | + Awaitility.await().atMost(Durations.FIVE_SECONDS).until(() -> dataSet.getNodes().size() > 50);
|
|---|
| 7431 | + // Actual test
|
|---|
| 7432 | + // With Mapillary, only ends of ways should be untagged
|
|---|
| 7433 | + // There are 55 actual "nodes" in the data with two nodes for the ends of the way.
|
|---|
| 7434 | + // One of the end nodes is a duplicate of an actual node.
|
|---|
| 7435 | + assertEquals(56, dataSet.getNodes().size());
|
|---|
| 7436 | + assertEquals(1, dataSet.getWays().size());
|
|---|
| 7437 | + assertEquals(0, dataSet.getRelations().size());
|
|---|
| 7438 | + }
|
|---|
| 7439 | +
|
|---|
| 7440 | + @Test
|
|---|
| 7441 | + void testWayDeduplicationSimple() {
|
|---|
| 7442 | + final VectorDataSet dataSet = this.layer.getData();
|
|---|
| 7443 | + assertTrue(dataSet.allPrimitives().isEmpty());
|
|---|
| 7444 | +
|
|---|
| 7445 | + // Set the zoom to 14, as that is the tile we are checking
|
|---|
| 7446 | + dataSet.setZoom(14);
|
|---|
| 7447 | + // Load tiles that are next to each other
|
|---|
| 7448 | + loadTile(this.layer, 14, 3248, 6258, 14, 3248, 6257);
|
|---|
| 7449 | +
|
|---|
| 7450 | + Map<Long, List<VectorWay>> wayGroups = dataSet.getWays().stream()
|
|---|
| 7451 | + .collect(Collectors.groupingBy(VectorWay::getId));
|
|---|
| 7452 | + wayGroups.forEach((id, ways) -> assertEquals(1, ways.size(), MessageFormat.format("{0} was not deduplicated", id)));
|
|---|
| 7453 | + }
|
|---|
| 7454 | +}
|
|---|
| 7455 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorNodeTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorNodeTest.java
|
|---|
| 7456 | new file mode 100644
|
|---|
| 7457 | index 000000000..834b46c4d
|
|---|
| 7458 | --- /dev/null
|
|---|
| 7459 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorNodeTest.java
|
|---|
| 7460 | @@ -0,0 +1,153 @@
|
|---|
| 7461 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 7462 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 7463 | +
|
|---|
| 7464 | +import org.junit.jupiter.api.Test;
|
|---|
| 7465 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 7466 | +import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
|---|
| 7467 | +import org.openstreetmap.josm.data.coor.EastNorth;
|
|---|
| 7468 | +import org.openstreetmap.josm.data.coor.LatLon;
|
|---|
| 7469 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 7470 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 7471 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 7472 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 7473 | +import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 7474 | +import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
|
|---|
| 7475 | +import org.openstreetmap.josm.data.projection.ProjectionRegistry;
|
|---|
| 7476 | +import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 7477 | +
|
|---|
| 7478 | +import java.util.ArrayList;
|
|---|
| 7479 | +import java.util.Collections;
|
|---|
| 7480 | +import java.util.List;
|
|---|
| 7481 | +
|
|---|
| 7482 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 7483 | +import static org.junit.jupiter.api.Assertions.assertFalse;
|
|---|
| 7484 | +import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|---|
| 7485 | +import static org.junit.jupiter.api.Assertions.assertSame;
|
|---|
| 7486 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 7487 | +import static org.junit.jupiter.api.Assertions.fail;
|
|---|
| 7488 | +
|
|---|
| 7489 | +/**
|
|---|
| 7490 | + * Test class for {@link VectorNode}
|
|---|
| 7491 | + * @author Taylor Smock
|
|---|
| 7492 | + * @since xxx
|
|---|
| 7493 | + */
|
|---|
| 7494 | +class VectorNodeTest {
|
|---|
| 7495 | + @RegisterExtension
|
|---|
| 7496 | + JOSMTestRules rule = new JOSMTestRules().projection();
|
|---|
| 7497 | +
|
|---|
| 7498 | + @Test
|
|---|
| 7499 | + void testLatLon() {
|
|---|
| 7500 | + VectorNode node = new VectorNode("test");
|
|---|
| 7501 | + assertTrue(Double.isNaN(node.lat()));
|
|---|
| 7502 | + assertTrue(Double.isNaN(node.lon()));
|
|---|
| 7503 | + LatLon testLatLon = new LatLon(50, -40);
|
|---|
| 7504 | + node.setCoor(testLatLon);
|
|---|
| 7505 | + assertEquals(50, node.lat());
|
|---|
| 7506 | + assertEquals(-40, node.lon());
|
|---|
| 7507 | + assertEquals(testLatLon, node.getCoor());
|
|---|
| 7508 | + }
|
|---|
| 7509 | +
|
|---|
| 7510 | + @Test
|
|---|
| 7511 | + void testSetEastNorth() {
|
|---|
| 7512 | + VectorNode node = new VectorNode("test");
|
|---|
| 7513 | + LatLon latLon = new LatLon(-1, 5);
|
|---|
| 7514 | + EastNorth eastNorth = ProjectionRegistry.getProjection().latlon2eastNorth(latLon);
|
|---|
| 7515 | + node.setEastNorth(eastNorth);
|
|---|
| 7516 | + assertEquals(-1, node.lat(), 0.0000000001);
|
|---|
| 7517 | + assertEquals(5, node.lon(), 0.0000000001);
|
|---|
| 7518 | + }
|
|---|
| 7519 | +
|
|---|
| 7520 | + @Test
|
|---|
| 7521 | + void testICoordinate() {
|
|---|
| 7522 | + VectorNode node = new VectorNode("test");
|
|---|
| 7523 | + assertTrue(Double.isNaN(node.lat()));
|
|---|
| 7524 | + assertTrue(Double.isNaN(node.lon()));
|
|---|
| 7525 | + ICoordinate coord = new ICoordinate() {
|
|---|
| 7526 | + @Override
|
|---|
| 7527 | + public double getLat() {
|
|---|
| 7528 | + return 5;
|
|---|
| 7529 | + }
|
|---|
| 7530 | +
|
|---|
| 7531 | + @Override
|
|---|
| 7532 | + public void setLat(double lat) {
|
|---|
| 7533 | + // No op
|
|---|
| 7534 | + }
|
|---|
| 7535 | +
|
|---|
| 7536 | + @Override
|
|---|
| 7537 | + public double getLon() {
|
|---|
| 7538 | + return -1;
|
|---|
| 7539 | + }
|
|---|
| 7540 | +
|
|---|
| 7541 | + @Override
|
|---|
| 7542 | + public void setLon(double lon) {
|
|---|
| 7543 | + // no op
|
|---|
| 7544 | + }
|
|---|
| 7545 | + };
|
|---|
| 7546 | + node.setCoor(coord);
|
|---|
| 7547 | + assertEquals(5, node.lat());
|
|---|
| 7548 | + assertEquals(-1, node.lon());
|
|---|
| 7549 | + }
|
|---|
| 7550 | +
|
|---|
| 7551 | + @Test
|
|---|
| 7552 | + void testUniqueIdGenerator() {
|
|---|
| 7553 | + VectorNode node1 = new VectorNode("test");
|
|---|
| 7554 | + VectorNode node2 = new VectorNode("test2");
|
|---|
| 7555 | + assertSame(node1.getIdGenerator(), node2.getIdGenerator());
|
|---|
| 7556 | + assertNotNull(node1.getIdGenerator());
|
|---|
| 7557 | + }
|
|---|
| 7558 | +
|
|---|
| 7559 | + @Test
|
|---|
| 7560 | + void testNode() {
|
|---|
| 7561 | + assertEquals(OsmPrimitiveType.NODE, new VectorNode("test").getType());
|
|---|
| 7562 | + }
|
|---|
| 7563 | +
|
|---|
| 7564 | + @Test
|
|---|
| 7565 | + void testBBox() {
|
|---|
| 7566 | + VectorNode node = new VectorNode("test");
|
|---|
| 7567 | + node.setCoor(new LatLon(5, -1));
|
|---|
| 7568 | + assertTrue(node.getBBox().bboxIsFunctionallyEqual(new BBox(-1, 5), 0d));
|
|---|
| 7569 | + }
|
|---|
| 7570 | +
|
|---|
| 7571 | + @Test
|
|---|
| 7572 | + void testVisitor() {
|
|---|
| 7573 | + List<VectorNode> visited = new ArrayList<>();
|
|---|
| 7574 | + VectorNode node = new VectorNode("test");
|
|---|
| 7575 | + node.accept(new PrimitiveVisitor() {
|
|---|
| 7576 | + @Override
|
|---|
| 7577 | + public void visit(INode n) {
|
|---|
| 7578 | + visited.add((VectorNode) n);
|
|---|
| 7579 | + }
|
|---|
| 7580 | +
|
|---|
| 7581 | + @Override
|
|---|
| 7582 | + public void visit(IWay<?> w) {
|
|---|
| 7583 | + fail("Way should not have been visited");
|
|---|
| 7584 | + }
|
|---|
| 7585 | +
|
|---|
| 7586 | + @Override
|
|---|
| 7587 | + public void visit(IRelation<?> r) {
|
|---|
| 7588 | + fail("Relation should not have been visited");
|
|---|
| 7589 | + }
|
|---|
| 7590 | + });
|
|---|
| 7591 | +
|
|---|
| 7592 | + assertEquals(1, visited.size());
|
|---|
| 7593 | + assertSame(node, visited.get(0));
|
|---|
| 7594 | + }
|
|---|
| 7595 | +
|
|---|
| 7596 | + @Test
|
|---|
| 7597 | + void testIsReferredToByWays() {
|
|---|
| 7598 | + VectorWay way = new VectorWay("test");
|
|---|
| 7599 | + VectorNode node = new VectorNode("test");
|
|---|
| 7600 | + assertFalse(node.isReferredByWays(1));
|
|---|
| 7601 | + assertTrue(node.getReferrers(true).isEmpty());
|
|---|
| 7602 | + way.setNodes(Collections.singletonList(node));
|
|---|
| 7603 | + assertEquals(1, node.getReferrers(true).size());
|
|---|
| 7604 | + assertSame(way, node.getReferrers(true).get(0));
|
|---|
| 7605 | + // No dataset yet
|
|---|
| 7606 | + assertFalse(node.isReferredByWays(1));
|
|---|
| 7607 | + VectorDataSet dataSet = new VectorDataSet();
|
|---|
| 7608 | + dataSet.addPrimitive(way);
|
|---|
| 7609 | + dataSet.addPrimitive(node);
|
|---|
| 7610 | + assertTrue(node.isReferredByWays(1));
|
|---|
| 7611 | + assertFalse(node.isReferredByWays(2));
|
|---|
| 7612 | + }
|
|---|
| 7613 | +}
|
|---|
| 7614 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorRelationTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorRelationTest.java
|
|---|
| 7615 | new file mode 100644
|
|---|
| 7616 | index 000000000..941143b25
|
|---|
| 7617 | --- /dev/null
|
|---|
| 7618 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorRelationTest.java
|
|---|
| 7619 | @@ -0,0 +1,45 @@
|
|---|
| 7620 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 7621 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 7622 | +
|
|---|
| 7623 | +import org.junit.jupiter.api.Test;
|
|---|
| 7624 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 7625 | +import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 7626 | +
|
|---|
| 7627 | +import java.util.Arrays;
|
|---|
| 7628 | +
|
|---|
| 7629 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 7630 | +import static org.junit.jupiter.api.Assertions.assertFalse;
|
|---|
| 7631 | +import static org.junit.jupiter.api.Assertions.assertSame;
|
|---|
| 7632 | +import static org.junit.jupiter.api.Assertions.assertThrows;
|
|---|
| 7633 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 7634 | +
|
|---|
| 7635 | +/**
|
|---|
| 7636 | + * Test class for {@link VectorRelation}
|
|---|
| 7637 | + * @author Taylor Smock
|
|---|
| 7638 | + * @since xxx
|
|---|
| 7639 | + */
|
|---|
| 7640 | +class VectorRelationTest {
|
|---|
| 7641 | + @RegisterExtension
|
|---|
| 7642 | + JOSMTestRules rule = new JOSMTestRules();
|
|---|
| 7643 | +
|
|---|
| 7644 | + @Test
|
|---|
| 7645 | + void testMembers() {
|
|---|
| 7646 | + VectorNode node1 = new VectorNode("test");
|
|---|
| 7647 | + VectorNode node2 = new VectorNode("test");
|
|---|
| 7648 | + VectorWay way1 = new VectorWay("test");
|
|---|
| 7649 | + way1.setNodes(Arrays.asList(node1, node2));
|
|---|
| 7650 | + VectorRelationMember member1 = new VectorRelationMember("randomRole", node1);
|
|---|
| 7651 | + VectorRelationMember member2 = new VectorRelationMember("role2", way1);
|
|---|
| 7652 | + assertSame(node1, member1.getMember());
|
|---|
| 7653 | + assertSame(node1.getType(), member1.getType());
|
|---|
| 7654 | + assertEquals("randomRole", member1.getRole());
|
|---|
| 7655 | + assertSame(node1.getId(), member1.getUniqueId());
|
|---|
| 7656 | + // Not a way.
|
|---|
| 7657 | + assertThrows(ClassCastException.class, member1::getWay);
|
|---|
| 7658 | +
|
|---|
| 7659 | + assertTrue(member1.isNode());
|
|---|
| 7660 | + assertFalse(member1.isWay());
|
|---|
| 7661 | + assertFalse(member2.isNode());
|
|---|
| 7662 | + assertTrue(member2.isWay());
|
|---|
| 7663 | + }
|
|---|
| 7664 | +}
|
|---|
| 7665 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorWayTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorWayTest.java
|
|---|
| 7666 | new file mode 100644
|
|---|
| 7667 | index 000000000..db2367e6b
|
|---|
| 7668 | --- /dev/null
|
|---|
| 7669 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorWayTest.java
|
|---|
| 7670 | @@ -0,0 +1,117 @@
|
|---|
| 7671 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 7672 | +package org.openstreetmap.josm.data.vector;
|
|---|
| 7673 | +
|
|---|
| 7674 | +import org.junit.jupiter.api.Test;
|
|---|
| 7675 | +import org.openstreetmap.josm.data.coor.LatLon;
|
|---|
| 7676 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 7677 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 7678 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 7679 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 7680 | +import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 7681 | +import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
|
|---|
| 7682 | +
|
|---|
| 7683 | +import java.util.ArrayList;
|
|---|
| 7684 | +import java.util.Arrays;
|
|---|
| 7685 | +import java.util.Collections;
|
|---|
| 7686 | +import java.util.List;
|
|---|
| 7687 | +
|
|---|
| 7688 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 7689 | +import static org.junit.jupiter.api.Assertions.assertFalse;
|
|---|
| 7690 | +import static org.junit.jupiter.api.Assertions.assertNull;
|
|---|
| 7691 | +import static org.junit.jupiter.api.Assertions.assertSame;
|
|---|
| 7692 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 7693 | +import static org.junit.jupiter.api.Assertions.fail;
|
|---|
| 7694 | +
|
|---|
| 7695 | +/**
|
|---|
| 7696 | + * Test class for {@link VectorWay}
|
|---|
| 7697 | + * @author Taylor Smock
|
|---|
| 7698 | + * @since xxx
|
|---|
| 7699 | + */
|
|---|
| 7700 | +class VectorWayTest {
|
|---|
| 7701 | + @Test
|
|---|
| 7702 | + void testBBox() {
|
|---|
| 7703 | + VectorNode node1 = new VectorNode("test");
|
|---|
| 7704 | + VectorWay way = new VectorWay("test");
|
|---|
| 7705 | + way.setNodes(Collections.singletonList(node1));
|
|---|
| 7706 | + node1.setCoor(new LatLon(-5, 1));
|
|---|
| 7707 | + assertTrue(node1.getBBox().bboxIsFunctionallyEqual(way.getBBox(), 0.0));
|
|---|
| 7708 | +
|
|---|
| 7709 | + VectorNode node2 = new VectorNode("test");
|
|---|
| 7710 | + node2.setCoor(new LatLon(-10, 2));
|
|---|
| 7711 | +
|
|---|
| 7712 | + way.setNodes(Arrays.asList(node1, node2));
|
|---|
| 7713 | + assertTrue(way.getBBox().bboxIsFunctionallyEqual(new BBox(2, -10, 1, -5), 0.0));
|
|---|
| 7714 | + }
|
|---|
| 7715 | +
|
|---|
| 7716 | + @Test
|
|---|
| 7717 | + void testIdGenerator() {
|
|---|
| 7718 | + assertSame(new VectorWay("test").getIdGenerator(), new VectorWay("test").getIdGenerator());
|
|---|
| 7719 | + }
|
|---|
| 7720 | +
|
|---|
| 7721 | + @Test
|
|---|
| 7722 | + void testNodes() {
|
|---|
| 7723 | + VectorNode node1 = new VectorNode("test");
|
|---|
| 7724 | + VectorNode node2 = new VectorNode("test");
|
|---|
| 7725 | + VectorNode node3 = new VectorNode("test");
|
|---|
| 7726 | + node1.setId(1);
|
|---|
| 7727 | + node2.setId(2);
|
|---|
| 7728 | + node3.setId(3);
|
|---|
| 7729 | + VectorWay way = new VectorWay("test");
|
|---|
| 7730 | + assertNull(way.firstNode());
|
|---|
| 7731 | + assertNull(way.lastNode());
|
|---|
| 7732 | + assertFalse(way.isClosed());
|
|---|
| 7733 | + assertFalse(way.isFirstLastNode(node1));
|
|---|
| 7734 | + assertFalse(way.isInnerNode(node2));
|
|---|
| 7735 | + way.setNodes(Arrays.asList(node1, node2, node3));
|
|---|
| 7736 | + assertEquals(3, way.getNodesCount());
|
|---|
| 7737 | + assertEquals(node1, way.getNode(0));
|
|---|
| 7738 | + assertEquals(node2, way.getNode(1));
|
|---|
| 7739 | + assertEquals(node3, way.getNode(2));
|
|---|
| 7740 | + assertTrue(way.isFirstLastNode(node1));
|
|---|
| 7741 | + assertTrue(way.isFirstLastNode(node3));
|
|---|
| 7742 | + assertFalse(way.isFirstLastNode(node2));
|
|---|
| 7743 | + assertTrue(way.isInnerNode(node2));
|
|---|
| 7744 | + assertFalse(way.isInnerNode(node1));
|
|---|
| 7745 | + assertFalse(way.isInnerNode(node3));
|
|---|
| 7746 | +
|
|---|
| 7747 | + assertEquals(1, way.getNodeIds().get(0));
|
|---|
| 7748 | + assertEquals(2, way.getNodeIds().get(1));
|
|---|
| 7749 | + assertEquals(3, way.getNodeIds().get(2));
|
|---|
| 7750 | + assertEquals(1, way.getNodeId(0));
|
|---|
| 7751 | + assertEquals(2, way.getNodeId(1));
|
|---|
| 7752 | + assertEquals(3, way.getNodeId(2));
|
|---|
| 7753 | +
|
|---|
| 7754 | + assertFalse(way.isClosed());
|
|---|
| 7755 | + assertEquals(OsmPrimitiveType.WAY, way.getType());
|
|---|
| 7756 | + List<VectorNode> nodes = new ArrayList<>(way.getNodes());
|
|---|
| 7757 | + nodes.add(nodes.get(0));
|
|---|
| 7758 | + way.setNodes(nodes);
|
|---|
| 7759 | + assertTrue(way.isClosed());
|
|---|
| 7760 | + assertEquals(OsmPrimitiveType.CLOSEDWAY, way.getType());
|
|---|
| 7761 | + }
|
|---|
| 7762 | +
|
|---|
| 7763 | + @Test
|
|---|
| 7764 | + void testAccept() {
|
|---|
| 7765 | + VectorWay way = new VectorWay("test");
|
|---|
| 7766 | + List<VectorWay> visited = new ArrayList<>(1);
|
|---|
| 7767 | + way.accept(new PrimitiveVisitor() {
|
|---|
| 7768 | + @Override
|
|---|
| 7769 | + public void visit(INode n) {
|
|---|
| 7770 | + fail("No nodes should be visited");
|
|---|
| 7771 | + }
|
|---|
| 7772 | +
|
|---|
| 7773 | + @Override
|
|---|
| 7774 | + public void visit(IWay<?> w) {
|
|---|
| 7775 | + visited.add((VectorWay) w);
|
|---|
| 7776 | + }
|
|---|
| 7777 | +
|
|---|
| 7778 | + @Override
|
|---|
| 7779 | + public void visit(IRelation<?> r) {
|
|---|
| 7780 | + fail("No relations should be visited");
|
|---|
| 7781 | + }
|
|---|
| 7782 | + });
|
|---|
| 7783 | +
|
|---|
| 7784 | + assertEquals(1, visited.size());
|
|---|
| 7785 | + assertSame(way, visited.get(0));
|
|---|
| 7786 | + }
|
|---|
| 7787 | +}
|
|---|
| 7788 | --
|
|---|
| 7789 | GitLab
|
|---|
| 7790 |
|
|---|
| 7791 |
|
|---|
| 7792 | From 60533498cd89f7b41b9e9822eb4b83ea55c60f7a Mon Sep 17 00:00:00 2001
|
|---|
| 7793 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 7794 | Date: Thu, 8 Apr 2021 16:45:20 -0600
|
|---|
| 7795 | Subject: [PATCH 06/50] Vector data test files (Mapillary)
|
|---|
| 7796 |
|
|---|
| 7797 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 7798 | ---
|
|---|
| 7799 | test/data/pbf/mapillary/14/3248/6258.mvt | Bin 0 -> 4256 bytes
|
|---|
| 7800 | test/data/pbf/mapillary/14/3249/6258.mvt | Bin 0 -> 8703 bytes
|
|---|
| 7801 | .../josm/data/vector/VectorDataSetTest.java | 2 +-
|
|---|
| 7802 | 3 files changed, 1 insertion(+), 1 deletion(-)
|
|---|
| 7803 | create mode 100644 test/data/pbf/mapillary/14/3248/6258.mvt
|
|---|
| 7804 | create mode 100644 test/data/pbf/mapillary/14/3249/6258.mvt
|
|---|
| 7805 |
|
|---|
| 7806 | diff --git a/test/data/pbf/mapillary/14/3248/6258.mvt b/test/data/pbf/mapillary/14/3248/6258.mvt
|
|---|
| 7807 | new file mode 100644
|
|---|
| 7808 | index 0000000000000000000000000000000000000000..ff6a462a3d79d0250c190bf9700e52dd35011d3c
|
|---|
| 7809 | GIT binary patch
|
|---|
| 7810 | literal 4256
|
|---|
| 7811 | zcmaLaeT>`W9S89CdO%xV#!API^f-=iv;~?rvE$fj=}Vl%vE#hIC+%SzpYt9k@k?T7
|
|---|
| 7812 | zXsQtF1_F&qs7MTi#29pJswy;1jG=0qs;;YGtERoM0a{_uy)oFPPK>TXd!G12>_4{q
|
|---|
| 7813 | zBRbvZ>)-Er9zXVTaeRAb;|*QD-|TdF*>EZ1KoccFR5m@dcKyZIe$d;ram|{wYiDNG
|
|---|
| 7814 | zt(%>ln{jFz4j=fy^YS`o^YYyCoZw(La}LfS%qQpg1#W?v6P%ng>^SHwE-X8joy`2Q
|
|---|
| 7815 | zlUs@}3QOR32fvkf#^*UF>)@ORfwGR}rGp^D1C7rwfBFgjvXxmV?<nugBEcQW-3;ki
|
|---|
| 7816 | z*A@-OY!iN7r75Bef4N%V`>H03D;%#nX8XL<bIxtNaiy=VNQe(H#y-BOYA8Bx;MEQk
|
|---|
| 7817 | zp(F7lwB7HunSjUzl?0aVHb+oMPczY?y~&1zU_vGkNi{^ZHgaybY0aZoJsN&tNIGX0
|
|---|
| 7818 | z*K9w$sIPT=xUlj1>1s8*d>t&+g1J)HO;_rQE3Uk9Zmsi*4d2^;KQi^Jz)G%%EBUFv
|
|---|
| 7819 | zzV8W(Zvej0=9l)qXz{CoUt{y5dtbHqwZN~l`PsYATl_P?uebSM_FS^~XMu0B`9pgi
|
|---|
| 7820 | zL#N}v0r+N{KezjY#Xkr9Mw|cIf5zgU2R>)>i^K(s-vr!Y^T&t>ywm<$fY00fEdHd$
|
|---|
| 7821 | zZw9_#^9#O{7QY4f;;P>|xj#pI=Pm98ew*$3A7Ym*z6AVso4@OQ5}S^1EAVYLf7tu7
|
|---|
| 7822 | z#kT{$!{*1)H!XfA@Vjh&4*A65JAk`vegt{KH|@U@_%53t^SoklH*k;5&$!Q99088n
|
|---|
| 7823 | z{L-$&__V$kIA-&syPmbU4>)e~mv)}EI04*m^AB9-ExsH09-BYxIzmkQ-wk}P&0pAY
|
|---|
| 7824 | z%;Nii@3;Azcb&5M0pJ0fU%vB##Yx~no1eJzuz%V=1Uzi>b9X#raSC{3)y@5Nar-HY
|
|---|
| 7825 | z)4&<q^+&f~uy_<WYxA?)4)31!j{%R{{Qa%ZT08+fY4b<7p0;=jc-rQtZ-2+)8Q@u)
|
|---|
| 7826 | zzq@o~&$NFIcz)GapP!{;7B2uV+OB`;wo?`_0WaIG|DE$s7Owz5X!FarK6LlA{~_SZ
|
|---|
| 7827 | zw))3!ea_<d0OxFeZt<+e?*-1={GYeHZ}BQ{!RE(q`O4mD{}te(%}*~Jw|EVB-R75X
|
|---|
| 7828 | ze%0a);LTMx*YBB||84OW@V3p*%s;np+P?$5Yx9d+Ubnaeyl3+hTOQayy}l28u<GXd
|
|---|
| 7829 | zbl!2&;xcf>=0_ce4>)E7-hSGe$1yx|9kYt<P`-Ac@tvk>aEj2AMR0`v{G;q2zWGt_
|
|---|
| 7830 | zfb}#5M{bN`Svg4Z9#sz!Z57x3Wp^zA&)NTfi2skHdS-DJH24XNKNxry^a2h1!)B!4
|
|---|
| 7831 | z8-@BS?uwt(DJfL16#^ji_IUI0$j4@=7?KDq7R!tc5>XhI4wQ?*XdQ(1WB0twTsA`j
|
|---|
| 7832 | zACFh+O}cF0y)3VZL!_K+ra{Q{;!__!|Be|V@=<R_8-_8)Th-hGGNRIQ8iu}n<8OcV
|
|---|
| 7833 | z(Hmw+mrJc`yHU*JO1*|gR&p*E--iEZ%oeV5z4#+DWMGke%1Dx!*b${lCs>XR@+chm
|
|---|
| 7834 | z=;YImUEeoDQLNpP(DazDj%g%Nt`eG>XJE+q^H$ILV`gZ873w03rJ7t)47EhsC<(X#
|
|---|
| 7835 | zX9wnX&CJ(V+p!51RYXCjd+lJDELG%YSca8eTKLjm)AyMngc9SGVX30C_%K$kqxF8Q
|
|---|
| 7836 | zTZ6OP=2`!p`TNaKge;HyisEK@cQBrer{xHqF2Z)HM;@0pWzA4C;3I^zuhy%&<9*E-
|
|---|
| 7837 | zwgU)1hV8cOdTZ%Q#th~BK943D^;}}$Z`5U0$#Y?E9c+$uAb8swAAhk$m_f8wZ2Mhh
|
|---|
| 7838 | z)FY<pRxZt^<YGmF)!sYjeB<MS8PZFEmV!29m7x5-T#b-@Jr;%IdN6SBd*>)KM7Od-
|
|---|
| 7839 | zL#SnX#F)jh%(xTvNGzP;AOG{MfBYhBhLAvk2*k#Di3~TiVWXd-IWY)B@2A0PMa&T1
|
|---|
| 7840 | zA~nR9Otjb@k9rCoa>V6z*zVF7Td%15%}}+9jYmW%5$?K~mC!)(Bn=sXmEcBr>Mk>s
|
|---|
| 7841 | zYmM?^kfLdWN|#D`9H;AI9L{dCr?!|OnJJTW56xq7ArTA_DoPb2E*MHwuP^!CW+<x=
|
|---|
| 7842 | zQCc$wqoSS^O1K`&bAce-cu$^MJRyh7P<*7X&?>D+F|okWky_3x^{cSbSAYDk?S}+2
|
|---|
| 7843 | zREi4~1uX<{Bu~W{i6}H`ZUGMbS31~vi)M(a(+Mxx>jX+fA}ceVXjny)F!TlR=)Jwu
|
|---|
| 7844 | z3=O(%oJ5U8&_7N!n@yvp<~&~5ZgPWTW{5Kg#zXbhU{-9GYpIB<5Y}CA;FH_ZG(+t~
|
|---|
| 7845 | zJ~oz-kSiOC31q~D6UCAWEBU{BW_Xe{L%qlfH;DVRkwFpJdH_omwL%hBIs~VeGD9k9
|
|---|
| 7846 | zRP{cd;6vV$SfdCwG6>gU2tImm9Wq0q_@I=o7SqL|Ah{^EuXQvn4MVTJ3Vyt1D4JpF
|
|---|
| 7847 | z-9U-U=na3M(TH<CEHr|lPksp&DsP4~AywrE4Zlk*;sr(!+I%ppz;(I<4(fmzs@61p
|
|---|
| 7848 | zW!UWz*)Wdi9a1K%UN@W_oZQo-85-3ocXx#2L$AB9MEg<Po2rFirOE7em?4&@+8MFa
|
|---|
| 7849 | z(2QI(#PhL??4cMK+VlJ5Bab^*+fgMWmGXL%bPsDq2CZS)MWZk@xxpnf<j>YUv^!E|
|
|---|
| 7850 | zw2@L$DIp$fHgz~A_&TVpKAdtl?@6hnV4cAjCKHRdBo97-OE5VS@YQx<Br#0ZDxqi{
|
|---|
| 7851 | zNi@@uVb)#t!O-4~zxhTVGeabWv;u5*RO>g?U`bcJNJjR<F@fDQ-n`EYX;Mqf8+xjk
|
|---|
| 7852 | z@pb6f5FH{yhlOLB?D5t8)M79?qcUX-_ovE*VpfcgIU0uk{`_eczsamr7WEv3#!4YC
|
|---|
| 7853 | z-jU>rJZ$GmlXjDbbM-|=w7qV**{akB9>Y&Z+)9-!5wOxEw9TyKB7!+jk8k^siss9)
|
|---|
| 7854 | zqqavS!|+`IPma^;%+NScN%S;-TdaU*S4Ms15Xn=p(&SipL7E(AcoLQ3G}tKtywT>;
|
|---|
| 7855 | z6s9BI$*C}T5HvG1lni<7%>`PB3mFdMiL6)7YOqq{Yk#|M{{}OJa4e@MB9Tmo?F^fV
|
|---|
| 7856 | zq^?ME>(kIryOVa2Vi60(!3(T5r1X)N$%cJ&0S=r5uS{pp3{^?+H9AK57+zK?Za&+P
|
|---|
| 7857 | z(vdc-G})P#I+IF43iAn^9BU$KMX3t;cEnFLVI>6)>S{Ao!3*V)XB0%E^>NFSAG_py
|
|---|
| 7858 | KI$xiB<NhzTVpF>S
|
|---|
| 7859 |
|
|---|
| 7860 | literal 0
|
|---|
| 7861 | HcmV?d00001
|
|---|
| 7862 |
|
|---|
| 7863 | diff --git a/test/data/pbf/mapillary/14/3249/6258.mvt b/test/data/pbf/mapillary/14/3249/6258.mvt
|
|---|
| 7864 | new file mode 100644
|
|---|
| 7865 | index 0000000000000000000000000000000000000000..c5278577ec4e161a9cb5acf787c0a3ec9d5ea6d5
|
|---|
| 7866 | GIT binary patch
|
|---|
| 7867 | literal 8703
|
|---|
| 7868 | zcmaKyd)OOQnZVo93oTGefl@wd`C1BXY11Z?%S?J@a-Uo#llw($oFtRUWpbS)Gno}d
|
|---|
| 7869 | zEl{9Ts1*?r70a!5Yh~H4)@~QOwRW*8mI9)Pii(K9mdd)Q$nH6FKFORvcJoJ{?|Xi~
|
|---|
| 7870 | zdEYnZoO#bV-_wal&zpB?lke0diSLa}vd|p}t&$)wymiL;Z~x%i?S=EEO`AS_#*FjM
|
|---|
| 7871 | zn>lmwjOA}lpL^2{`)BQ&GkvxxXbP?muMb}1um-H2)fDO3=vq&>H@P?1ulGAb>!NEn
|
|---|
| 7872 | zPsXluFU?G5CL<Gt$->gm1V5RZ3@t9`zn4~kpH^(Uj+xxHDzo;3X7{R$EwjPBF18^;
|
|---|
| 7873 | zZC#h35}Tasq8k!hVw-4lluB)Kugy5<wF&T3Y{S-du`L^kh$rMDDR;mfv~Qu=t-<w_
|
|---|
| 7874 | zGe8FIfo0}38`f-CX<NT#rP=DTfZFUqGPq`w-A4vZn@Ep^^18emoY#lUDS}>}SYB8f
|
|---|
| 7875 | zUA%3gG!eb5FcDk4ZE<NLvZ%CV+mgtl!jkFJCeCXJqluX{`0t#7EcD<H7nJx;f6x=k
|
|---|
| 7876 | z1-?Hqv%|OA%NNgERPGGQEy6*JGzVYn53E+LN){zZN>Yngtc^~)!O%j%EwlDmv!+1e
|
|---|
| 7877 | zveejmz!t5x2zx0``?;#nuPDpsUODZqId^&Y4PDD;Oio*M)1*3m;>S14TQHugT9X&S
|
|---|
| 7878 | zJS|1jG=Drzvu4eny?FZaS##gp`uMCdp97}xd^nBo^QX7IYv6MMpEt!X(4T*B>xtRp
|
|---|
| 7879 | z=Pv~OBJBBdTXxSG^Z9^ZjPYYzUNrCpfM0^~2e#}!fBgJHz%Rx4Yny*+;Fke@ImQod
|
|---|
| 7880 | zeqiqS`9*+Vf$^Q2pEK~qfM1F6Gn?+4H-3Hs@T)L>@1_?Gd<ozm!1${hcU>@kektHr
|
|---|
| 7881 | zV|?$%rwn`&@MReP<%aDSj-Ou+_%#^cx8Z<+uK@g7jKA#sje%bW_)3i5<J^7GIQ}ZY
|
|---|
| 7882 | zS7ZDHb=1JG2Yk&GNAtgfdda}o0&c>dKSXYyKaRf+@bwr!=XlV-&461lexKu*ffImR
|
|---|
| 7883 | zF@DVcvVq$Gw`2Tm+q(wt0G!159^3sFkL#lVcVher>tO@m0Qkl!j=uk^#2EwM1o&p`
|
|---|
| 7884 | z`CEv$4SWmWTQR=hvS-0K{|$iC82_btzk#~|cVqk}^C<)O0Pe;3<Lh5Ba3A1)jGtb2
|
|---|
| 7885 | z%O&Id0l*oIziHZM;6cDc7=O@o%)rBdM=*YD?JEY(0v^TqZ`SNwIL^-j9-HEEy}#VI
|
|---|
| 7886 | zX1{?a08e84$n|FoJOy|fkH7k+OULmufM+qjWA#A;&jFst`0-UgG4Kxp{vnLNzH;Yf
|
|---|
| 7887 | z<M`VE|1ie)tUO`h1;9Uo@u#nQ+rW9iix_|J+5?x5<Cg$0WBlN?uNb%hcm?BUSL|6d
|
|---|
| 7888 | zeqIE;it+nboHFnl;B}0jyk^H0<L4WIOBmmE%^?GC0^Y*-(dB0iybX8<<F73{uy`E5
|
|---|
| 7889 | z3wRIX_bz+Qz-7Su7=Lc^zAMMi4*(xx{Fcen2Ce|EV*KFMyC%lZj{w&&{`%6x2L2C#
|
|---|
| 7890 | ze-z_;mYy~68v*|q#!r9X{;L*VjrQM*r+pmoo2FyJdrQt-wQv#%3#NTyIxs$o8;>rz
|
|---|
| 7891 | zYsuL76g0Nu#&cIaWiW1r#x1yU&sFajj9a0x12>LO9QwdG%WcrO9XEDNylOD+fW}VT
|
|---|
| 7892 | zIDF;)rQ;ZPLSq+hyt(+C!T2;Z?!t{ji}zhUj&V0McH_pmE6y2=&p=}jZXCGc$mBT2
|
|---|
| 7893 | zXQ6QqZoISTF9zdt(6|>j4lO#lY#ihB(6|pb{&e~7<zwRu(6}ErPF((!!T2IH9>9&A
|
|---|
| 7894 | zm+!u29OFyS_%d#sx$H%Q@fB!1h#L=Fws*xi##f=S7dPI$^o+rH2pSLL#?zPXx^^7n
|
|---|
| 7895 | z5okP$8+$K3X)qpx#y;G5cj3DR<7?2^j~k~J9=>jz<sYGO05|q7e9d5d9U2F5<Lo8-
|
|---|
| 7896 | zSB_(R0~(Lx#?ed88jOE}#uK=)=aNTPjbr>XG!Eg$OAF2!jDLZ~H*w?P1xHqoV|)u5
|
|---|
| 7897 | zhjHUC7r$dLz735dxN-dAqt}mP{3|q$;>OO4cdQv3{|1dGapR@=#|_4Jpm7X09-hBr
|
|---|
| 7898 | z?KsB2L*qDZynWG=2IITXIDs1nF8ZWt9OHY?IQe%2ZPc$`c*J1*2Q;4gyMeA62QGZY
|
|---|
| 7899 | zV0<4Mr*Pxt3+`Dr&hi6j{17*GUU1T2{0JIP<Hl3-UNaa!hQ?{!_|x2->&IE1fyNo!
|
|---|
| 7900 | zxM%J@gYhghp2Lk3=N~f|&qL!Uxbf<o7Y)V>(D*5CY@hQdgYh$Hyoei5%)Z?`uJq^7
|
|---|
| 7901 | zcnLSo%-U-(egTbN;>IUu9WfZcg2v0Zv3KSfgYj!<{029kJnv0|@mpxTf*a>%Y`2Un
|
|---|
| 7902 | z{T(!3#f@DvzHBgl4~^Gw<KXlomcMV63#R=68m}9TnRAP$4Z<5xcoP?9ylI)3QR4Bd
|
|---|
| 7903 | zH@cg{`}f358M~oh)E)3rtv@QrrFKsM_vr;$=MyLM^&5;=YjFP#g<NjQQ*mnvx6^K`
|
|---|
| 7904 | zF^$To*Hz%9`~Sbu{~!1E8Iv<Xg3<4<vz*@nxj+J?Q4TsY`Dm<QWf^OW3Wgl*WDH2n
|
|---|
| 7905 | z-~IKmw{Az$z;Dk>!w%&uGu~V=oa;qBf<FeTI1h+F`tQWAANnd1<AIh`57dhtvs#Wf
|
|---|
| 7906 | z)S_MLRu~aT>G`I|zxXvINd%n=aG?P$MznOyPg8Xz+@OK<y%2dD{SK1YVai&Oq-HwR
|
|---|
| 7907 | z^^<D8Qz_f?Jy;hk>-3ckz4&m{(<Ia8f~JYI)s<yDoZIGwQt-kVYi_+BNy$js6m$sH
|
|---|
| 7908 | zPBJLlSZ}uM$@t6gPZ)5-hj}Cg;svu*5m+@76!^Z?RZYvB7Z!eRYvHG!+>Ioq;~ud!
|
|---|
| 7909 | zzp9WW$wHbvQcAP8VLJHb{_`0mHNw6$7mv9`zB+W(Jh>Q4P%bEa=+npkYn3YLg_lx^
|
|---|
| 7910 | zPJpR&G>b-Rt&CNQc$^Vf)199K`3@qflM))Tn#o68b|+&hsshy|z}(Nha^G90BgL~w
|
|---|
| 7911 | zQV2_2j241iqvNf$#aLS%XhS%ddf|(jdOFeTZ4HVFpNZ2ZZ>Qp*hs;2OHT@zA5*|Pj
|
|---|
| 7912 | zWo0YLKs=dg)<()G8{##d)W6+EX#E9AvG`Dx**HzqA|tM=nl)$LNx*SB(*MljnQTi>
|
|---|
| 7913 | zS11kTs#d0bCc9a**J+aVWuh?Mja%&>K71pRM3t8d9nr^mvw>2q?B{ZJr3b&=kC*@K
|
|---|
| 7914 | z&mVgZNoH%ZGICjk21%8~R>oH5>a_%nbpP?mgT1wFJ)NiE%Z2Gc)1PET2kDBKB0N8Y
|
|---|
| 7915 | zHT_Dv?0}y^QZ>wxWYJ8e%$*w9_xDO3n_piltKqyPkrYdeLQyj<xT0QHL+(=celJ^q
|
|---|
| 7916 | zgDF?<e&eGel8Qt%)QiY<Hc=ZE>r@p)OR%seeF%0VDc2gvb+cS;t8Cg|i^m8qk*z{$
|
|---|
| 7917 | z8vNXaq;kVw4=aq#<Fw^LR8v3T@zr4AAlX*?$EG6DD$}E4v4Yp7wbIU3$k$^fSkpI`
|
|---|
| 7918 | z-u#DH7D?<-Bg&odut%6Hsyme_RD`+=2lH7Kyz4h1sg&-~{Z`*2^cXU#rg$ci>`G87
|
|---|
| 7919 | z!x=6h$>PzhLNioOWQ*Qf)!TK~z2OK<_k&a5^HC&qHJ;!Tb=l$xTjGiq%sA4N2n(;n
|
|---|
| 7920 | zF_@Ap{(h&`tkzjVvE>NbD+#n)h3Q`SA?O$}6e-`RSzP&aQg!f>$z|pgrZLnbodZjJ
|
|---|
| 7921 | z!FD7u)}lKfA60WMQ&Zt+Ic8F<J(%wMU-2ILqa8_6t!5t7TK$kF`K?Z`<TY2@1vuiC
|
|---|
| 7922 | z^Y<^wPa>&8$)p-4h=3FnYI3NRE(E(>DCu40Ewt!t)`(y7IbxP>SanH#mzT?xC@AR-
|
|---|
| 7923 | z<1~`O5*Kfmg+|{~P)+`7kDzS9m<U?pbRhovp)VYt+>S(7!J15kn>M294HJ1sqv~r7
|
|---|
| 7924 | zt8hT!lG?frNm{a)jMj#1pym#=X`i1pdj$?k`s!SZq+wcQQmR_vy!lEcYf=jB(I5n+
|
|---|
| 7925 | zWe0oJyZ?+{&@$^E<*QsvbH(^(FI?{r`a}_qx;|FlKvI}b^232Wku<SwF&=bu>VjWi
|
|---|
| 7926 | z!l2hZ^_9;fsnRy(%qFGI`TS}(CfgV{-)g~hXCFBcA~z$68VOc8qGftbt174!UM_J(
|
|---|
| 7927 | zCzSLzrj4YAFB@qGMlGV3WE8?Ax1$|VA9ejzcOfb6kOMufASY-~miDv$W;o=mz(~)4
|
|---|
| 7928 | zg^;}&No5P)j#fFfE!I=MZqF>3)9oN!32;Z-&rL-tcWQ1yrHN+Vn~M--yO^%T;rt!?
|
|---|
| 7929 | z<KNDxP9zcGPQx3uG3h8B8f8iyuI3U+D1GF&PcQ%3B}f9V66<JMdpy%2L+M0NELdVJ
|
|---|
| 7930 | ztV!RkHy|nRsPHrq3ze8)e%O_pk%7zFhtkhi%>Kk@H%>{apUY5%CL8hyxma^3hsi+(
|
|---|
| 7931 | z7S6$r??;k7S&4y-zUuBp99FZ@3u!kSjXHJnD$KpK{{As?$Uv?zD8rh>wntT0_0
|
|---|
| 7932 | zPF@X3qiiyftAf#rni$?yiWXIKw*u3FZE?{HyO2bf+)T}u)2LjV6r!18VZel&Fr9ur
|
|---|
| 7933 | zIE5rjxGoGWRF0&gu3$cBl6s282iH>tejZ1XRg1(;-qMg^Rj*S`QX|^o(>w4z?mML4
|
|---|
| 7934 | z$w+D?yD_yxMMq*u86=ggJe2!cm=129kKB%=fG1TRIFvv?l8HBqY`ehJa(V~W`#}~-
|
|---|
| 7935 | zVJ*T?0dptF1cDVQpQ|XjWFOY_aoB&tND^qjP4PNxyk%e!T<s=TWYYRhr5{DMBB>It
|
|---|
| 7936 | z2h8b2fZ^p_W@N3!vo$6JBk4=!4Yb9o7LUMp<XkpD`5c})RZglJ3kOqQ<6lQoG1P4|
|
|---|
| 7937 | z6_2ypX;M93C^s;t{Q)>FaoDGCL{hVF8a5J2f=-tc)@a+t#{=CKjHF8uBqhuWT`5&9
|
|---|
| 7938 | zO|e^K9a^eVk5~1Ep&yUVP8~&jM9e<)2`<hOH<c|Tu2Zu*U^=~~=aFP{jxrn-$$>Ku
|
|---|
| 7939 | zA=PqaFUxu@?~nLVByn^pAo|o?qapa*X(n7{giabt`tErrlBz?qoA#wzTwjSZ8fEtm
|
|---|
| 7940 | zd=4*62U=@+<JXa7FFQ!3AqEK{EM^_;%uq1<G$`o}e=1Tqk*qcpN{N_?=|nTfCPz^*
|
|---|
| 7941 | z3MGBSV<?hF(_El#j(dGtv}E$O$u=7v!J72_<%X%6_>`EhI0o(@*KZbzqi}<d^r57m
|
|---|
| 7942 | z4VECO7pl~AK}PMF8<|3cbH(g3O~Z70>zR$Dve5GNB4sz99oB_pv>O<edU+U0f6W&m
|
|---|
| 7943 | zDJPLmcg-%@L7N*E6LO_AYU#UzUehF!Y$0nSUX2Q*7VO#`X35!zWJ55L-dLST3S~2j
|
|---|
| 7944 | zi%ty^DSJ0R$a#vis~U$j-S>I$VURSa<P}Bk6btdVj}}vwPNG!Tdx3r~uONvs)kJ!f
|
|---|
| 7945 | zHPzd>P=(3n*mm73z;ycIuZSdpY_>|SKtImA)Gpf?3`lQk0MmW=FmvII^+?J)g+iM5
|
|---|
| 7946 | z<XK9U*s`M$CEIrWY;YS58saPHOz!D>B(3XmSMBLaz~boBVKzR3k@T7-P$WhjmRt2q
|
|---|
| 7947 | zk+V0uTp<$>`UQ)A^3i+293+La9kRuZ+AYb|ETozaMjKWW@4r%0H3=p;(;qecETKi?
|
|---|
| 7948 | zt|-k%5*9a<!r+?lk<}<tHJb0aQ*_ByNjF-}K~-s{do@@SXgq&?Y#EXga*{FGf`xRs
|
|---|
| 7949 | zBADum%T0|&B^c=nxHMNHsqSiLYZ}FrWw}0ZH1j<v$=Bduz60(zU+^Hw+;xT$jY=Zh
|
|---|
| 7950 | zQ-X=0&z^`=R()maZ}4}K6b%&}RkGWY>r5(6DXnsh)AVgaIr^oE^%nG+^G*xX=)`(_
|
|---|
| 7951 | zq)4UeUOPz_dKH)sZsu>?g`{e|*=t(5?)<<z2*(`mP+o}yV1Lpt6O*Wkm82xW3@z4J
|
|---|
| 7952 | zs#OW+JWbxr`ipP~^ds92B)NPe(KHgRWwSQ$nIq;vqei6Q5Wq#S=6{eBX0lSL;mt<n
|
|---|
| 7953 | zQZ&j1J-jFv(olL3oU{IR13HFu$y#0$iCmsF$HWqo9JV5jHcY24x&ufuMZ{`_OvI=*
|
|---|
| 7954 | z39cG7ksS1k8Y~>{KR>(!oyJmZsZ6Wkpw%C1rHD$pK*pnHI0WD-k)Ho8Bxxb9YA^U~
|
|---|
| 7955 | zU0=T!lm}d;oQtMmB>h@)6iHploz^NXQjD~#^>##}2du20?)7nc9Z3m47xz`meQ&hU
|
|---|
| 7956 | zOWUX*mvssWSU9+E)Q?O_oRAJne8lg|vflon(@f_jO~01tXSyk=m*A6@QX^xP`Anb@
|
|---|
| 7957 | zjE}^APu~&rmH8}6M+#O`l;(1IpKNb~<6>DfrNi*sJ^$6t=6TN}sbA~+xu{y{G;@?t
|
|---|
| 7958 | zOnVyPbY0&O^saIiNkmfwou^GEOJc#=baL51cw~V?pdXE1K$7I?iX5G@4&p?T5Odj*
|
|---|
| 7959 | z*dEp2Ur(oy<m=k2_5o{&ID&48B}t{%BWO6mdb9ryl6u*`KT_vArVf#h`U*LBP?Yp(
|
|---|
| 7960 | z(Kn!zNOBZRik9<pVaneh3|qCDlFDRYP4ArnSB|N<OKC}ynvQVEe%@M*D!e<Lcc`!?
|
|---|
| 7961 | h{aW%96sZ=<g~4A7N~XRy=OLW^ysCt)ivE8J{uhw)>!Sbw
|
|---|
| 7962 |
|
|---|
| 7963 | literal 0
|
|---|
| 7964 | HcmV?d00001
|
|---|
| 7965 |
|
|---|
| 7966 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 7967 | index 8983e8397..38ab53ad2 100644
|
|---|
| 7968 | --- a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 7969 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 7970 | @@ -132,7 +132,7 @@ class VectorDataSetTest {
|
|---|
| 7971 | // Set the zoom to 14, as that is the tile we are checking
|
|---|
| 7972 | dataSet.setZoom(14);
|
|---|
| 7973 | // Load tiles that are next to each other
|
|---|
| 7974 | - loadTile(this.layer, 14, 3248, 6258, 14, 3248, 6257);
|
|---|
| 7975 | + loadTile(this.layer, 14, 3248, 6258, 14, 3249, 6258);
|
|---|
| 7976 |
|
|---|
| 7977 | Map<Long, List<VectorWay>> wayGroups = dataSet.getWays().stream()
|
|---|
| 7978 | .collect(Collectors.groupingBy(VectorWay::getId));
|
|---|
| 7979 | --
|
|---|
| 7980 | GitLab
|
|---|
| 7981 |
|
|---|
| 7982 |
|
|---|
| 7983 | From 25dfa842314b093a39bb34169c82fa95b72938b7 Mon Sep 17 00:00:00 2001
|
|---|
| 7984 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 7985 | Date: Thu, 8 Apr 2021 15:56:47 -0600
|
|---|
| 7986 | Subject: [PATCH 07/50] Other frontend items
|
|---|
| 7987 |
|
|---|
| 7988 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 7989 | ---
|
|---|
| 7990 | resources/images/dialogs/add_mvt.svg | 147 +++++++++
|
|---|
| 7991 | .../data/cache/JCSCachedTileLoaderJob.java | 47 ++-
|
|---|
| 7992 | .../josm/data/imagery/ImageryInfo.java | 8 +-
|
|---|
| 7993 | .../data/imagery/TMSCachedTileLoaderJob.java | 10 +-
|
|---|
| 7994 | .../josm/data/osm/IRelationMember.java | 9 +
|
|---|
| 7995 | .../josm/data/osm/IWaySegment.java | 177 +++++++++++
|
|---|
| 7996 | .../josm/data/osm/RelationMember.java | 1 +
|
|---|
| 7997 | .../josm/data/osm/WaySegment.java | 99 +------
|
|---|
| 7998 | .../osm/visitor/paint/StyledMapRenderer.java | 7 +-
|
|---|
| 7999 | .../relation/sort/RelationNodeMap.java | 73 ++---
|
|---|
| 8000 | .../relation/sort/RelationSortUtils.java | 32 +-
|
|---|
| 8001 | .../dialogs/relation/sort/RelationSorter.java | 10 +-
|
|---|
| 8002 | .../gui/layer/AbstractTileSourceLayer.java | 30 +-
|
|---|
| 8003 | .../josm/gui/layer/ImageryLayer.java | 3 +
|
|---|
| 8004 | .../josm/gui/layer/imagery/MVTLayer.java | 278 ++++++++++++++++++
|
|---|
| 8005 | .../gui/mappaint/mapcss/ConditionFactory.java | 11 +
|
|---|
| 8006 | .../preferences/imagery/AddMVTLayerPanel.java | 94 ++++++
|
|---|
| 8007 | .../imagery/ImageryProvidersPanel.java | 11 +-
|
|---|
| 8008 | 18 files changed, 891 insertions(+), 156 deletions(-)
|
|---|
| 8009 | create mode 100644 resources/images/dialogs/add_mvt.svg
|
|---|
| 8010 | create mode 100644 src/org/openstreetmap/josm/data/osm/IWaySegment.java
|
|---|
| 8011 | create mode 100644 src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 8012 | create mode 100644 src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java
|
|---|
| 8013 |
|
|---|
| 8014 | diff --git a/resources/images/dialogs/add_mvt.svg b/resources/images/dialogs/add_mvt.svg
|
|---|
| 8015 | new file mode 100644
|
|---|
| 8016 | index 000000000..eeae80f10
|
|---|
| 8017 | --- /dev/null
|
|---|
| 8018 | +++ b/resources/images/dialogs/add_mvt.svg
|
|---|
| 8019 | @@ -0,0 +1,147 @@
|
|---|
| 8020 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|---|
| 8021 | +<svg
|
|---|
| 8022 | + xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|---|
| 8023 | + xmlns:cc="http://creativecommons.org/ns#"
|
|---|
| 8024 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|---|
| 8025 | + xmlns="http://www.w3.org/2000/svg"
|
|---|
| 8026 | + xmlns:xlink="http://www.w3.org/1999/xlink"
|
|---|
| 8027 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|---|
| 8028 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|---|
| 8029 | + width="24"
|
|---|
| 8030 | + height="24"
|
|---|
| 8031 | + viewBox="0 0 24 24"
|
|---|
| 8032 | + id="svg2"
|
|---|
| 8033 | + version="1.1"
|
|---|
| 8034 | + inkscape:version="1.0.1 (c497b03c, 2020-09-10)"
|
|---|
| 8035 | + sodipodi:docname="add_mvt.svg">
|
|---|
| 8036 | + <defs
|
|---|
| 8037 | + id="defs4">
|
|---|
| 8038 | + <linearGradient
|
|---|
| 8039 | + gradientTransform="translate(4)"
|
|---|
| 8040 | + gradientUnits="userSpaceOnUse"
|
|---|
| 8041 | + y2="1049.3622"
|
|---|
| 8042 | + x2="12"
|
|---|
| 8043 | + y1="1041.3622"
|
|---|
| 8044 | + x1="4"
|
|---|
| 8045 | + id="linearGradient868"
|
|---|
| 8046 | + xlink:href="#linearGradient866"
|
|---|
| 8047 | + inkscape:collect="always" />
|
|---|
| 8048 | + <linearGradient
|
|---|
| 8049 | + id="linearGradient866"
|
|---|
| 8050 | + inkscape:collect="always">
|
|---|
| 8051 | + <stop
|
|---|
| 8052 | + id="stop862"
|
|---|
| 8053 | + offset="0"
|
|---|
| 8054 | + style="stop-color:#dfdfdf;stop-opacity:1" />
|
|---|
| 8055 | + <stop
|
|---|
| 8056 | + id="stop864"
|
|---|
| 8057 | + offset="1"
|
|---|
| 8058 | + style="stop-color:#949593;stop-opacity:1" />
|
|---|
| 8059 | + </linearGradient>
|
|---|
| 8060 | + </defs>
|
|---|
| 8061 | + <sodipodi:namedview
|
|---|
| 8062 | + id="base"
|
|---|
| 8063 | + pagecolor="#ffffff"
|
|---|
| 8064 | + bordercolor="#666666"
|
|---|
| 8065 | + borderopacity="1.0"
|
|---|
| 8066 | + inkscape:pageopacity="0"
|
|---|
| 8067 | + inkscape:pageshadow="2"
|
|---|
| 8068 | + inkscape:zoom="45.254834"
|
|---|
| 8069 | + inkscape:cx="11.376506"
|
|---|
| 8070 | + inkscape:cy="17.057298"
|
|---|
| 8071 | + inkscape:document-units="px"
|
|---|
| 8072 | + inkscape:current-layer="layer1"
|
|---|
| 8073 | + showgrid="true"
|
|---|
| 8074 | + units="px"
|
|---|
| 8075 | + inkscape:window-width="1920"
|
|---|
| 8076 | + inkscape:window-height="955"
|
|---|
| 8077 | + inkscape:window-x="0"
|
|---|
| 8078 | + inkscape:window-y="23"
|
|---|
| 8079 | + inkscape:window-maximized="1"
|
|---|
| 8080 | + viewbox-height="16"
|
|---|
| 8081 | + inkscape:document-rotation="0">
|
|---|
| 8082 | + <inkscape:grid
|
|---|
| 8083 | + type="xygrid"
|
|---|
| 8084 | + id="grid4136"
|
|---|
| 8085 | + originx="0"
|
|---|
| 8086 | + originy="0"
|
|---|
| 8087 | + spacingx="1"
|
|---|
| 8088 | + spacingy="1" />
|
|---|
| 8089 | + </sodipodi:namedview>
|
|---|
| 8090 | + <metadata
|
|---|
| 8091 | + id="metadata7">
|
|---|
| 8092 | + <rdf:RDF>
|
|---|
| 8093 | + <cc:Work
|
|---|
| 8094 | + rdf:about="">
|
|---|
| 8095 | + <dc:format>image/svg+xml</dc:format>
|
|---|
| 8096 | + <dc:type
|
|---|
| 8097 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|---|
| 8098 | + <dc:title></dc:title>
|
|---|
| 8099 | + <cc:license
|
|---|
| 8100 | + rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
|
|---|
| 8101 | + </cc:Work>
|
|---|
| 8102 | + <cc:License
|
|---|
| 8103 | + rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
|
|---|
| 8104 | + <cc:permits
|
|---|
| 8105 | + rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
|---|
| 8106 | + <cc:permits
|
|---|
| 8107 | + rdf:resource="http://creativecommons.org/ns#Distribution" />
|
|---|
| 8108 | + <cc:permits
|
|---|
| 8109 | + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
|---|
| 8110 | + </cc:License>
|
|---|
| 8111 | + </rdf:RDF>
|
|---|
| 8112 | + </metadata>
|
|---|
| 8113 | + <g
|
|---|
| 8114 | + inkscape:label="Layer 1"
|
|---|
| 8115 | + inkscape:groupmode="layer"
|
|---|
| 8116 | + id="layer1"
|
|---|
| 8117 | + transform="translate(0,-1037.3622)">
|
|---|
| 8118 | + <rect
|
|---|
| 8119 | + ry="0.48361239"
|
|---|
| 8120 | + y="1043.8622"
|
|---|
| 8121 | + x="5.5"
|
|---|
| 8122 | + height="3"
|
|---|
| 8123 | + width="13"
|
|---|
| 8124 | + id="rect833"
|
|---|
| 8125 | + style="opacity:1;fill:#c1c2c0;fill-opacity:1;fill-rule:nonzero;stroke:#555753;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.839909;stroke-opacity:1;paint-order:normal" />
|
|---|
| 8126 | + <rect
|
|---|
| 8127 | + transform="rotate(-90)"
|
|---|
| 8128 | + ry="0.48361239"
|
|---|
| 8129 | + y="10.5"
|
|---|
| 8130 | + x="-1051.8622"
|
|---|
| 8131 | + height="3"
|
|---|
| 8132 | + width="13"
|
|---|
| 8133 | + id="rect833-5"
|
|---|
| 8134 | + style="opacity:1;fill:#c1c2c0;fill-opacity:1;fill-rule:nonzero;stroke:#555753;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.839909;stroke-opacity:1;paint-order:normal" />
|
|---|
| 8135 | + <path
|
|---|
| 8136 | + inkscape:connector-curvature="0"
|
|---|
| 8137 | + id="path852"
|
|---|
| 8138 | + d="M 6.0000001,1044.3622 H 11 v -5 h 2 v 5 h 5 v 2 h -5 v 5 h -2 v -5 H 6.0000001 Z"
|
|---|
| 8139 | + style="fill:url(#linearGradient868);fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|---|
| 8140 | + <path
|
|---|
| 8141 | + style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
|---|
| 8142 | + d="m 4.5,1060.3625 v -7.5948 l 2,4.3971 2,-4.3971 v 7.5948"
|
|---|
| 8143 | + id="path894"
|
|---|
| 8144 | + sodipodi:nodetypes="ccccc" />
|
|---|
| 8145 | + <path
|
|---|
| 8146 | + style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|---|
| 8147 | + d="m 17.5,1060.3622 v -8"
|
|---|
| 8148 | + id="path896" />
|
|---|
| 8149 | + <path
|
|---|
| 8150 | + style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
|---|
| 8151 | + d="m 15,1052.8622 h 5"
|
|---|
| 8152 | + id="path898" />
|
|---|
| 8153 | + <text
|
|---|
| 8154 | + xml:space="preserve"
|
|---|
| 8155 | + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:9.3042px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.894202;stroke-miterlimit:4;stroke-dasharray:none"
|
|---|
| 8156 | + x="10.59868"
|
|---|
| 8157 | + y="898.41876"
|
|---|
| 8158 | + id="text854"
|
|---|
| 8159 | + transform="scale(0.84728029,1.180247)"><tspan
|
|---|
| 8160 | + sodipodi:role="line"
|
|---|
| 8161 | + id="tspan852"
|
|---|
| 8162 | + x="10.59868"
|
|---|
| 8163 | + y="898.41876"
|
|---|
| 8164 | + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:9.3042px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill-rule:nonzero;stroke-width:0.894202;stroke-miterlimit:4;stroke-dasharray:none">V</tspan></text>
|
|---|
| 8165 | + </g>
|
|---|
| 8166 | +</svg>
|
|---|
| 8167 | diff --git a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 8168 | index a8561a771..eeac761c6 100644
|
|---|
| 8169 | --- a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 8170 | +++ b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 8171 | @@ -1,10 +1,13 @@
|
|---|
| 8172 | // License: GPL. For details, see LICENSE file.
|
|---|
| 8173 | package org.openstreetmap.josm.data.cache;
|
|---|
| 8174 |
|
|---|
| 8175 | +import java.io.File;
|
|---|
| 8176 | import java.io.FileNotFoundException;
|
|---|
| 8177 | import java.io.IOException;
|
|---|
| 8178 | +import java.io.InputStream;
|
|---|
| 8179 | import java.net.HttpURLConnection;
|
|---|
| 8180 | import java.net.URL;
|
|---|
| 8181 | +import java.nio.file.Files;
|
|---|
| 8182 | import java.security.SecureRandom;
|
|---|
| 8183 | import java.util.Collections;
|
|---|
| 8184 | import java.util.List;
|
|---|
| 8185 | @@ -17,8 +20,6 @@ import java.util.concurrent.ThreadPoolExecutor;
|
|---|
| 8186 | import java.util.concurrent.TimeUnit;
|
|---|
| 8187 | import java.util.regex.Matcher;
|
|---|
| 8188 |
|
|---|
| 8189 | -import org.apache.commons.jcs3.access.behavior.ICacheAccess;
|
|---|
| 8190 | -import org.apache.commons.jcs3.engine.behavior.ICacheElement;
|
|---|
| 8191 | import org.openstreetmap.josm.data.cache.ICachedLoaderListener.LoadResult;
|
|---|
| 8192 | import org.openstreetmap.josm.data.imagery.TileJobOptions;
|
|---|
| 8193 | import org.openstreetmap.josm.data.preferences.IntegerProperty;
|
|---|
| 8194 | @@ -27,6 +28,10 @@ import org.openstreetmap.josm.tools.HttpClient;
|
|---|
| 8195 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 8196 | import org.openstreetmap.josm.tools.Utils;
|
|---|
| 8197 |
|
|---|
| 8198 | +import org.apache.commons.compress.utils.IOUtils;
|
|---|
| 8199 | +import org.apache.commons.jcs3.access.behavior.ICacheAccess;
|
|---|
| 8200 | +import org.apache.commons.jcs3.engine.behavior.ICacheElement;
|
|---|
| 8201 | +
|
|---|
| 8202 | /**
|
|---|
| 8203 | * Generic loader for HTTP based tiles. Uses custom attribute, to check, if entry has expired
|
|---|
| 8204 | * according to HTTP headers sent with tile. If so, it tries to verify using Etags
|
|---|
| 8205 | @@ -294,6 +299,43 @@ public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements
|
|---|
| 8206 | if (attributes == null) {
|
|---|
| 8207 | attributes = new CacheEntryAttributes();
|
|---|
| 8208 | }
|
|---|
| 8209 | + final URL url = this.getUrlNoException();
|
|---|
| 8210 | + if (url == null) {
|
|---|
| 8211 | + return false;
|
|---|
| 8212 | + }
|
|---|
| 8213 | +
|
|---|
| 8214 | + if (url.getProtocol().contains("http")) {
|
|---|
| 8215 | + return loadObjectHttp();
|
|---|
| 8216 | + }
|
|---|
| 8217 | + if (url.getProtocol().contains("file")) {
|
|---|
| 8218 | + return loadObjectFile(url);
|
|---|
| 8219 | + }
|
|---|
| 8220 | +
|
|---|
| 8221 | + return false;
|
|---|
| 8222 | + }
|
|---|
| 8223 | +
|
|---|
| 8224 | + private boolean loadObjectFile(URL url) {
|
|---|
| 8225 | + String fileName = url.toExternalForm();
|
|---|
| 8226 | + File file = new File(fileName.substring("file:/".length() - 1));
|
|---|
| 8227 | + if (!file.exists()) {
|
|---|
| 8228 | + file = new File(fileName.substring("file://".length() - 1));
|
|---|
| 8229 | + }
|
|---|
| 8230 | + try (InputStream fileInputStream = Files.newInputStream(file.toPath())) {
|
|---|
| 8231 | + cacheData = createCacheEntry(IOUtils.toByteArray(fileInputStream));
|
|---|
| 8232 | + cache.put(getCacheKey(), cacheData, attributes);
|
|---|
| 8233 | + return true;
|
|---|
| 8234 | + } catch (IOException e) {
|
|---|
| 8235 | + Logging.error(e);
|
|---|
| 8236 | + attributes.setError(e);
|
|---|
| 8237 | + attributes.setException(e);
|
|---|
| 8238 | + }
|
|---|
| 8239 | + return false;
|
|---|
| 8240 | + }
|
|---|
| 8241 | +
|
|---|
| 8242 | + /**
|
|---|
| 8243 | + * @return true if object was successfully downloaded via http, false, if there was a loading failure
|
|---|
| 8244 | + */
|
|---|
| 8245 | + private boolean loadObjectHttp() {
|
|---|
| 8246 | try {
|
|---|
| 8247 | // if we have object in cache, and host doesn't support If-Modified-Since nor If-None-Match
|
|---|
| 8248 | // then just use HEAD request and check returned values
|
|---|
| 8249 | @@ -553,6 +595,7 @@ public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements
|
|---|
| 8250 | try {
|
|---|
| 8251 | return getUrl();
|
|---|
| 8252 | } catch (IOException e) {
|
|---|
| 8253 | + Logging.trace(e);
|
|---|
| 8254 | return null;
|
|---|
| 8255 | }
|
|---|
| 8256 | }
|
|---|
| 8257 | diff --git a/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java b/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
|
|---|
| 8258 | index 07cabc76a..32b1055ed 100644
|
|---|
| 8259 | --- a/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
|
|---|
| 8260 | +++ b/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
|
|---|
| 8261 | @@ -61,7 +61,9 @@ public class ImageryInfo extends
|
|---|
| 8262 | /** A WMS endpoint entry only stores the WMS server info, without layer, which are chosen later by the user. **/
|
|---|
| 8263 | WMS_ENDPOINT("wms_endpoint"),
|
|---|
| 8264 | /** WMTS stores GetCapabilities URL. Does not store any information about the layer **/
|
|---|
| 8265 | - WMTS("wmts");
|
|---|
| 8266 | + WMTS("wmts"),
|
|---|
| 8267 | + /** MapBox Vector Tiles entry*/
|
|---|
| 8268 | + MVT("mvt");
|
|---|
| 8269 |
|
|---|
| 8270 | private final String typeString;
|
|---|
| 8271 |
|
|---|
| 8272 | @@ -654,7 +656,7 @@ public class ImageryInfo extends
|
|---|
| 8273 | defaultMaxZoom = 0;
|
|---|
| 8274 | defaultMinZoom = 0;
|
|---|
| 8275 | for (ImageryType type : ImageryType.values()) {
|
|---|
| 8276 | - Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url);
|
|---|
| 8277 | + Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)])?:(.*)").matcher(url);
|
|---|
| 8278 | if (m.matches()) {
|
|---|
| 8279 | this.url = m.group(3);
|
|---|
| 8280 | this.sourceType = type;
|
|---|
| 8281 | @@ -669,7 +671,7 @@ public class ImageryInfo extends
|
|---|
| 8282 | }
|
|---|
| 8283 |
|
|---|
| 8284 | if (serverProjections.isEmpty()) {
|
|---|
| 8285 | - Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)\\}.*").matcher(url.toUpperCase(Locale.ENGLISH));
|
|---|
| 8286 | + Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)}.*").matcher(url.toUpperCase(Locale.ENGLISH));
|
|---|
| 8287 | if (m.matches()) {
|
|---|
| 8288 | setServerProjections(Arrays.asList(m.group(1).split(",", -1)));
|
|---|
| 8289 | }
|
|---|
| 8290 | diff --git a/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
|
|---|
| 8291 | index e9f163781..1f2c0d1d7 100644
|
|---|
| 8292 | --- a/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
|
|---|
| 8293 | +++ b/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
|
|---|
| 8294 | @@ -10,6 +10,7 @@ import java.net.URL;
|
|---|
| 8295 | import java.nio.charset.StandardCharsets;
|
|---|
| 8296 | import java.util.HashSet;
|
|---|
| 8297 | import java.util.List;
|
|---|
| 8298 | +import java.util.Locale;
|
|---|
| 8299 | import java.util.Map;
|
|---|
| 8300 | import java.util.Map.Entry;
|
|---|
| 8301 | import java.util.Optional;
|
|---|
| 8302 | @@ -33,6 +34,8 @@ import org.openstreetmap.josm.data.cache.CacheEntry;
|
|---|
| 8303 | import org.openstreetmap.josm.data.cache.CacheEntryAttributes;
|
|---|
| 8304 | import org.openstreetmap.josm.data.cache.ICachedLoaderListener;
|
|---|
| 8305 | import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob;
|
|---|
| 8306 | +import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 8307 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTFile;
|
|---|
| 8308 | import org.openstreetmap.josm.data.preferences.LongProperty;
|
|---|
| 8309 | import org.openstreetmap.josm.tools.HttpClient;
|
|---|
| 8310 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 8311 | @@ -149,7 +152,7 @@ public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe
|
|---|
| 8312 | private boolean isNotImage(Map<String, List<String>> headers, int statusCode) {
|
|---|
| 8313 | if (statusCode == 200 && headers.containsKey("Content-Type") && !headers.get("Content-Type").isEmpty()) {
|
|---|
| 8314 | String contentType = headers.get("Content-Type").stream().findAny().get();
|
|---|
| 8315 | - if (contentType != null && !contentType.startsWith("image")) {
|
|---|
| 8316 | + if (contentType != null && !contentType.startsWith("image") && !MVTFile.MIMETYPE.contains(contentType.toLowerCase(Locale.ROOT))) {
|
|---|
| 8317 | Logging.warn("Image not returned for tile: " + url + " content type was: " + contentType);
|
|---|
| 8318 | // not an image - do not store response in cache, so next time it will be queried again from the server
|
|---|
| 8319 | return true;
|
|---|
| 8320 | @@ -320,10 +323,11 @@ public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe
|
|---|
| 8321 | private boolean tryLoadTileImage(CacheEntry object) throws IOException {
|
|---|
| 8322 | if (object != null) {
|
|---|
| 8323 | byte[] content = object.getContent();
|
|---|
| 8324 | - if (content.length > 0) {
|
|---|
| 8325 | + if (content.length > 0 || tile instanceof VectorTile) {
|
|---|
| 8326 | try (ByteArrayInputStream in = new ByteArrayInputStream(content)) {
|
|---|
| 8327 | tile.loadImage(in);
|
|---|
| 8328 | - if (tile.getImage() == null) {
|
|---|
| 8329 | + if ((!(tile instanceof VectorTile) && tile.getImage() == null)
|
|---|
| 8330 | + || ((tile instanceof VectorTile) && !tile.isLoaded())) {
|
|---|
| 8331 | String s = new String(content, StandardCharsets.UTF_8);
|
|---|
| 8332 | Matcher m = SERVICE_EXCEPTION_PATTERN.matcher(s);
|
|---|
| 8333 | if (m.matches()) {
|
|---|
| 8334 | diff --git a/src/org/openstreetmap/josm/data/osm/IRelationMember.java b/src/org/openstreetmap/josm/data/osm/IRelationMember.java
|
|---|
| 8335 | index c2803e38d..69091056d 100644
|
|---|
| 8336 | --- a/src/org/openstreetmap/josm/data/osm/IRelationMember.java
|
|---|
| 8337 | +++ b/src/org/openstreetmap/josm/data/osm/IRelationMember.java
|
|---|
| 8338 | @@ -66,4 +66,13 @@ public interface IRelationMember<P extends IPrimitive> extends PrimitiveId {
|
|---|
| 8339 | * @since 13766 (IRelationMember)
|
|---|
| 8340 | */
|
|---|
| 8341 | P getMember();
|
|---|
| 8342 | +
|
|---|
| 8343 | + /**
|
|---|
| 8344 | + * Returns the relation member as a way.
|
|---|
| 8345 | + * @return Member as a way
|
|---|
| 8346 | + * @since xxx
|
|---|
| 8347 | + */
|
|---|
| 8348 | + default IWay<?> getWay() {
|
|---|
| 8349 | + return (IWay<?>) getMember();
|
|---|
| 8350 | + }
|
|---|
| 8351 | }
|
|---|
| 8352 | diff --git a/src/org/openstreetmap/josm/data/osm/IWaySegment.java b/src/org/openstreetmap/josm/data/osm/IWaySegment.java
|
|---|
| 8353 | new file mode 100644
|
|---|
| 8354 | index 000000000..0735935de
|
|---|
| 8355 | --- /dev/null
|
|---|
| 8356 | +++ b/src/org/openstreetmap/josm/data/osm/IWaySegment.java
|
|---|
| 8357 | @@ -0,0 +1,177 @@
|
|---|
| 8358 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 8359 | +package org.openstreetmap.josm.data.osm;
|
|---|
| 8360 | +
|
|---|
| 8361 | +import java.awt.geom.Line2D;
|
|---|
| 8362 | +import java.lang.reflect.Constructor;
|
|---|
| 8363 | +import java.lang.reflect.InvocationTargetException;
|
|---|
| 8364 | +import java.util.Arrays;
|
|---|
| 8365 | +import java.util.Objects;
|
|---|
| 8366 | +
|
|---|
| 8367 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 8368 | +
|
|---|
| 8369 | +/**
|
|---|
| 8370 | + * A segment consisting of 2 consecutive nodes out of a way.
|
|---|
| 8371 | + * @author Taylor Smock
|
|---|
| 8372 | + * @param <N> The node type
|
|---|
| 8373 | + * @param <W> The way type
|
|---|
| 8374 | + * @since xxx
|
|---|
| 8375 | + */
|
|---|
| 8376 | +public class IWaySegment<N extends INode, W extends IWay<N>> implements Comparable<IWaySegment<N, W>> {
|
|---|
| 8377 | +
|
|---|
| 8378 | + /**
|
|---|
| 8379 | + * The way.
|
|---|
| 8380 | + */
|
|---|
| 8381 | + public final W way;
|
|---|
| 8382 | +
|
|---|
| 8383 | + /**
|
|---|
| 8384 | + * The index of one of the 2 nodes in the way. The other node has the
|
|---|
| 8385 | + * index <code>lowerIndex + 1</code>.
|
|---|
| 8386 | + */
|
|---|
| 8387 | + public final int lowerIndex;
|
|---|
| 8388 | +
|
|---|
| 8389 | + /**
|
|---|
| 8390 | + * Constructs a new {@code IWaySegment}.
|
|---|
| 8391 | + * @param w The way
|
|---|
| 8392 | + * @param i The node lower index
|
|---|
| 8393 | + * @throws IllegalArgumentException in case of invalid index
|
|---|
| 8394 | + */
|
|---|
| 8395 | + public IWaySegment(W w, int i) {
|
|---|
| 8396 | + way = w;
|
|---|
| 8397 | + lowerIndex = i;
|
|---|
| 8398 | + if (i < 0 || i >= w.getNodesCount() - 1) {
|
|---|
| 8399 | + throw new IllegalArgumentException(toString());
|
|---|
| 8400 | + }
|
|---|
| 8401 | + }
|
|---|
| 8402 | +
|
|---|
| 8403 | + /**
|
|---|
| 8404 | + * Returns the first node of the way segment.
|
|---|
| 8405 | + * @return the first node
|
|---|
| 8406 | + */
|
|---|
| 8407 | + public N getFirstNode() {
|
|---|
| 8408 | + return way.getNode(lowerIndex);
|
|---|
| 8409 | + }
|
|---|
| 8410 | +
|
|---|
| 8411 | + /**
|
|---|
| 8412 | + * Returns the second (last) node of the way segment.
|
|---|
| 8413 | + * @return the second node
|
|---|
| 8414 | + */
|
|---|
| 8415 | + public N getSecondNode() {
|
|---|
| 8416 | + return way.getNode(lowerIndex + 1);
|
|---|
| 8417 | + }
|
|---|
| 8418 | +
|
|---|
| 8419 | + /**
|
|---|
| 8420 | + * Determines and returns the way segment for the given way and node pair.
|
|---|
| 8421 | + * @param way way
|
|---|
| 8422 | + * @param first first node
|
|---|
| 8423 | + * @param second second node
|
|---|
| 8424 | + * @return way segment
|
|---|
| 8425 | + * @throws IllegalArgumentException if the node pair is not part of way
|
|---|
| 8426 | + */
|
|---|
| 8427 | + public static <N extends INode, W extends IWay<N>> IWaySegment<N, W> forNodePair(W way, N first, N second) {
|
|---|
| 8428 | + int endIndex = way.getNodesCount() - 1;
|
|---|
| 8429 | + while (endIndex > 0) {
|
|---|
| 8430 | + final int indexOfFirst = way.getNodes().subList(0, endIndex).lastIndexOf(first);
|
|---|
| 8431 | + if (second.equals(way.getNode(indexOfFirst + 1))) {
|
|---|
| 8432 | + return new IWaySegment<>(way, indexOfFirst);
|
|---|
| 8433 | + }
|
|---|
| 8434 | + endIndex--;
|
|---|
| 8435 | + }
|
|---|
| 8436 | + throw new IllegalArgumentException("Node pair is not part of way!");
|
|---|
| 8437 | + }
|
|---|
| 8438 | +
|
|---|
| 8439 | + /**
|
|---|
| 8440 | + * Returns this way segment as complete way.
|
|---|
| 8441 | + * @return the way segment as {@code Way}
|
|---|
| 8442 | + * @throws IllegalAccessException See {@link Constructor#newInstance}
|
|---|
| 8443 | + * @throws IllegalArgumentException See {@link Constructor#newInstance}
|
|---|
| 8444 | + * @throws InstantiationException See {@link Constructor#newInstance}
|
|---|
| 8445 | + * @throws InvocationTargetException See {@link Constructor#newInstance}
|
|---|
| 8446 | + * @throws NoSuchMethodException See {@link Class#getConstructor}
|
|---|
| 8447 | + */
|
|---|
| 8448 | + public W toWay()
|
|---|
| 8449 | + throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
|
|---|
| 8450 | + // If the number of nodes is 2, then don't bother creating a new way
|
|---|
| 8451 | + if (this.way.getNodes().size() == 2) {
|
|---|
| 8452 | + return this.way;
|
|---|
| 8453 | + }
|
|---|
| 8454 | + // Since the way determines the generic class, this.way.getClass() is always Class<W>, assuming
|
|---|
| 8455 | + // that way remains the defining element for the type, and remains final.
|
|---|
| 8456 | + @SuppressWarnings("unchecked")
|
|---|
| 8457 | + Class<W> clazz = (Class<W>) this.way.getClass();
|
|---|
| 8458 | + Constructor<W> constructor;
|
|---|
| 8459 | + W w;
|
|---|
| 8460 | + try {
|
|---|
| 8461 | + // Check for clone constructor
|
|---|
| 8462 | + constructor = clazz.getConstructor(clazz);
|
|---|
| 8463 | + w = constructor.newInstance(this.way);
|
|---|
| 8464 | + } catch (NoSuchMethodException e) {
|
|---|
| 8465 | + Logging.trace(e);
|
|---|
| 8466 | + constructor = clazz.getConstructor();
|
|---|
| 8467 | + w = constructor.newInstance();
|
|---|
| 8468 | + }
|
|---|
| 8469 | +
|
|---|
| 8470 | + w.setNodes(Arrays.asList(getFirstNode(), getSecondNode()));
|
|---|
| 8471 | + return w;
|
|---|
| 8472 | + }
|
|---|
| 8473 | +
|
|---|
| 8474 | + @Override
|
|---|
| 8475 | + public boolean equals(Object o) {
|
|---|
| 8476 | + if (this == o) return true;
|
|---|
| 8477 | + if (o == null || getClass() != o.getClass()) return false;
|
|---|
| 8478 | + IWaySegment<?, ?> that = (IWaySegment<?, ?>) o;
|
|---|
| 8479 | + return lowerIndex == that.lowerIndex &&
|
|---|
| 8480 | + Objects.equals(way, that.way);
|
|---|
| 8481 | + }
|
|---|
| 8482 | +
|
|---|
| 8483 | + @Override
|
|---|
| 8484 | + public int hashCode() {
|
|---|
| 8485 | + return Objects.hash(way, lowerIndex);
|
|---|
| 8486 | + }
|
|---|
| 8487 | +
|
|---|
| 8488 | + @Override
|
|---|
| 8489 | + public int compareTo(IWaySegment o) {
|
|---|
| 8490 | + final W thisWay;
|
|---|
| 8491 | + final IWay<?> otherWay;
|
|---|
| 8492 | + try {
|
|---|
| 8493 | + thisWay = toWay();
|
|---|
| 8494 | + otherWay = o == null ? null : o.toWay();
|
|---|
| 8495 | + } catch (ReflectiveOperationException e) {
|
|---|
| 8496 | + Logging.error(e);
|
|---|
| 8497 | + return -1;
|
|---|
| 8498 | + }
|
|---|
| 8499 | + return o == null ? -1 : (equals(o) ? 0 : thisWay.compareTo(otherWay));
|
|---|
| 8500 | + }
|
|---|
| 8501 | +
|
|---|
| 8502 | + /**
|
|---|
| 8503 | + * Checks whether this segment crosses other segment
|
|---|
| 8504 | + *
|
|---|
| 8505 | + * @param s2 The other segment
|
|---|
| 8506 | + * @return true if both segments crosses
|
|---|
| 8507 | + */
|
|---|
| 8508 | + public boolean intersects(IWaySegment<?, ?> s2) {
|
|---|
| 8509 | + if (getFirstNode().equals(s2.getFirstNode()) || getSecondNode().equals(s2.getSecondNode()) ||
|
|---|
| 8510 | + getFirstNode().equals(s2.getSecondNode()) || getSecondNode().equals(s2.getFirstNode()))
|
|---|
| 8511 | + return false;
|
|---|
| 8512 | +
|
|---|
| 8513 | + return Line2D.linesIntersect(
|
|---|
| 8514 | + getFirstNode().getEastNorth().east(), getFirstNode().getEastNorth().north(),
|
|---|
| 8515 | + getSecondNode().getEastNorth().east(), getSecondNode().getEastNorth().north(),
|
|---|
| 8516 | + s2.getFirstNode().getEastNorth().east(), s2.getFirstNode().getEastNorth().north(),
|
|---|
| 8517 | + s2.getSecondNode().getEastNorth().east(), s2.getSecondNode().getEastNorth().north());
|
|---|
| 8518 | + }
|
|---|
| 8519 | +
|
|---|
| 8520 | + /**
|
|---|
| 8521 | + * Checks whether this segment and another way segment share the same points
|
|---|
| 8522 | + * @param s2 The other segment
|
|---|
| 8523 | + * @return true if other way segment is the same or reverse
|
|---|
| 8524 | + */
|
|---|
| 8525 | + public boolean isSimilar(IWaySegment<?, ?> s2) {
|
|---|
| 8526 | + return (getFirstNode().equals(s2.getFirstNode()) && getSecondNode().equals(s2.getSecondNode()))
|
|---|
| 8527 | + || (getFirstNode().equals(s2.getSecondNode()) && getSecondNode().equals(s2.getFirstNode()));
|
|---|
| 8528 | + }
|
|---|
| 8529 | +
|
|---|
| 8530 | + @Override
|
|---|
| 8531 | + public String toString() {
|
|---|
| 8532 | + return "IWaySegment [way=" + way.getUniqueId() + ", lowerIndex=" + lowerIndex + ']';
|
|---|
| 8533 | + }
|
|---|
| 8534 | +}
|
|---|
| 8535 | diff --git a/src/org/openstreetmap/josm/data/osm/RelationMember.java b/src/org/openstreetmap/josm/data/osm/RelationMember.java
|
|---|
| 8536 | index fc62c71f3..5add40403 100644
|
|---|
| 8537 | --- a/src/org/openstreetmap/josm/data/osm/RelationMember.java
|
|---|
| 8538 | +++ b/src/org/openstreetmap/josm/data/osm/RelationMember.java
|
|---|
| 8539 | @@ -57,6 +57,7 @@ public class RelationMember implements IRelationMember<OsmPrimitive> {
|
|---|
| 8540 | * @return Member as way
|
|---|
| 8541 | * @since 1937
|
|---|
| 8542 | */
|
|---|
| 8543 | + @Override
|
|---|
| 8544 | public Way getWay() {
|
|---|
| 8545 | return (Way) member;
|
|---|
| 8546 | }
|
|---|
| 8547 | diff --git a/src/org/openstreetmap/josm/data/osm/WaySegment.java b/src/org/openstreetmap/josm/data/osm/WaySegment.java
|
|---|
| 8548 | index 2ca1cc379..302f82842 100644
|
|---|
| 8549 | --- a/src/org/openstreetmap/josm/data/osm/WaySegment.java
|
|---|
| 8550 | +++ b/src/org/openstreetmap/josm/data/osm/WaySegment.java
|
|---|
| 8551 | @@ -1,57 +1,26 @@
|
|---|
| 8552 | // License: GPL. For details, see LICENSE file.
|
|---|
| 8553 | package org.openstreetmap.josm.data.osm;
|
|---|
| 8554 |
|
|---|
| 8555 | -import java.awt.geom.Line2D;
|
|---|
| 8556 | -import java.util.Objects;
|
|---|
| 8557 | -
|
|---|
| 8558 | /**
|
|---|
| 8559 | * A segment consisting of 2 consecutive nodes out of a way.
|
|---|
| 8560 | */
|
|---|
| 8561 | -public final class WaySegment implements Comparable<WaySegment> {
|
|---|
| 8562 | -
|
|---|
| 8563 | - /**
|
|---|
| 8564 | - * The way.
|
|---|
| 8565 | - */
|
|---|
| 8566 | - public final Way way;
|
|---|
| 8567 | -
|
|---|
| 8568 | - /**
|
|---|
| 8569 | - * The index of one of the 2 nodes in the way. The other node has the
|
|---|
| 8570 | - * index <code>lowerIndex + 1</code>.
|
|---|
| 8571 | - */
|
|---|
| 8572 | - public final int lowerIndex;
|
|---|
| 8573 | +public final class WaySegment extends IWaySegment<Node, Way> {
|
|---|
| 8574 |
|
|---|
| 8575 | /**
|
|---|
| 8576 | - * Constructs a new {@code WaySegment}.
|
|---|
| 8577 | - * @param w The way
|
|---|
| 8578 | - * @param i The node lower index
|
|---|
| 8579 | + * Constructs a new {@code IWaySegment}.
|
|---|
| 8580 | + *
|
|---|
| 8581 | + * @param way The way
|
|---|
| 8582 | + * @param i The node lower index
|
|---|
| 8583 | * @throws IllegalArgumentException in case of invalid index
|
|---|
| 8584 | */
|
|---|
| 8585 | - public WaySegment(Way w, int i) {
|
|---|
| 8586 | - way = w;
|
|---|
| 8587 | - lowerIndex = i;
|
|---|
| 8588 | - if (i < 0 || i >= w.getNodesCount() - 1) {
|
|---|
| 8589 | - throw new IllegalArgumentException(toString());
|
|---|
| 8590 | - }
|
|---|
| 8591 | + public WaySegment(Way way, int i) {
|
|---|
| 8592 | + super(way, i);
|
|---|
| 8593 | }
|
|---|
| 8594 |
|
|---|
| 8595 | /**
|
|---|
| 8596 | - * Returns the first node of the way segment.
|
|---|
| 8597 | - * @return the first node
|
|---|
| 8598 | - */
|
|---|
| 8599 | - public Node getFirstNode() {
|
|---|
| 8600 | - return way.getNode(lowerIndex);
|
|---|
| 8601 | - }
|
|---|
| 8602 | -
|
|---|
| 8603 | - /**
|
|---|
| 8604 | - * Returns the second (last) node of the way segment.
|
|---|
| 8605 | - * @return the second node
|
|---|
| 8606 | - */
|
|---|
| 8607 | - public Node getSecondNode() {
|
|---|
| 8608 | - return way.getNode(lowerIndex + 1);
|
|---|
| 8609 | - }
|
|---|
| 8610 | -
|
|---|
| 8611 | - /**
|
|---|
| 8612 | - * Determines and returns the way segment for the given way and node pair.
|
|---|
| 8613 | + * Determines and returns the way segment for the given way and node pair. You should prefer
|
|---|
| 8614 | + * {@link IWaySegment#forNodePair(IWay, INode, INode)} whenever possible.
|
|---|
| 8615 | + *
|
|---|
| 8616 | * @param way way
|
|---|
| 8617 | * @param first first node
|
|---|
| 8618 | * @param second second node
|
|---|
| 8619 | @@ -74,6 +43,7 @@ public final class WaySegment implements Comparable<WaySegment> {
|
|---|
| 8620 | * Returns this way segment as complete way.
|
|---|
| 8621 | * @return the way segment as {@code Way}
|
|---|
| 8622 | */
|
|---|
| 8623 | + @Override
|
|---|
| 8624 | public Way toWay() {
|
|---|
| 8625 | Way w = new Way();
|
|---|
| 8626 | w.addNode(getFirstNode());
|
|---|
| 8627 | @@ -81,53 +51,6 @@ public final class WaySegment implements Comparable<WaySegment> {
|
|---|
| 8628 | return w;
|
|---|
| 8629 | }
|
|---|
| 8630 |
|
|---|
| 8631 | - @Override
|
|---|
| 8632 | - public boolean equals(Object o) {
|
|---|
| 8633 | - if (this == o) return true;
|
|---|
| 8634 | - if (o == null || getClass() != o.getClass()) return false;
|
|---|
| 8635 | - WaySegment that = (WaySegment) o;
|
|---|
| 8636 | - return lowerIndex == that.lowerIndex &&
|
|---|
| 8637 | - Objects.equals(way, that.way);
|
|---|
| 8638 | - }
|
|---|
| 8639 | -
|
|---|
| 8640 | - @Override
|
|---|
| 8641 | - public int hashCode() {
|
|---|
| 8642 | - return Objects.hash(way, lowerIndex);
|
|---|
| 8643 | - }
|
|---|
| 8644 | -
|
|---|
| 8645 | - @Override
|
|---|
| 8646 | - public int compareTo(WaySegment o) {
|
|---|
| 8647 | - return o == null ? -1 : (equals(o) ? 0 : toWay().compareTo(o.toWay()));
|
|---|
| 8648 | - }
|
|---|
| 8649 | -
|
|---|
| 8650 | - /**
|
|---|
| 8651 | - * Checks whether this segment crosses other segment
|
|---|
| 8652 | - *
|
|---|
| 8653 | - * @param s2 The other segment
|
|---|
| 8654 | - * @return true if both segments crosses
|
|---|
| 8655 | - */
|
|---|
| 8656 | - public boolean intersects(WaySegment s2) {
|
|---|
| 8657 | - if (getFirstNode().equals(s2.getFirstNode()) || getSecondNode().equals(s2.getSecondNode()) ||
|
|---|
| 8658 | - getFirstNode().equals(s2.getSecondNode()) || getSecondNode().equals(s2.getFirstNode()))
|
|---|
| 8659 | - return false;
|
|---|
| 8660 | -
|
|---|
| 8661 | - return Line2D.linesIntersect(
|
|---|
| 8662 | - getFirstNode().getEastNorth().east(), getFirstNode().getEastNorth().north(),
|
|---|
| 8663 | - getSecondNode().getEastNorth().east(), getSecondNode().getEastNorth().north(),
|
|---|
| 8664 | - s2.getFirstNode().getEastNorth().east(), s2.getFirstNode().getEastNorth().north(),
|
|---|
| 8665 | - s2.getSecondNode().getEastNorth().east(), s2.getSecondNode().getEastNorth().north());
|
|---|
| 8666 | - }
|
|---|
| 8667 | -
|
|---|
| 8668 | - /**
|
|---|
| 8669 | - * Checks whether this segment and another way segment share the same points
|
|---|
| 8670 | - * @param s2 The other segment
|
|---|
| 8671 | - * @return true if other way segment is the same or reverse
|
|---|
| 8672 | - */
|
|---|
| 8673 | - public boolean isSimilar(WaySegment s2) {
|
|---|
| 8674 | - return (getFirstNode().equals(s2.getFirstNode()) && getSecondNode().equals(s2.getSecondNode()))
|
|---|
| 8675 | - || (getFirstNode().equals(s2.getSecondNode()) && getSecondNode().equals(s2.getFirstNode()));
|
|---|
| 8676 | - }
|
|---|
| 8677 | -
|
|---|
| 8678 | @Override
|
|---|
| 8679 | public String toString() {
|
|---|
| 8680 | return "WaySegment [way=" + way.getUniqueId() + ", lowerIndex=" + lowerIndex + ']';
|
|---|
| 8681 | diff --git a/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java b/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
|
|---|
| 8682 | index 038374233..03ba0e5b2 100644
|
|---|
| 8683 | --- a/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
|
|---|
| 8684 | +++ b/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
|
|---|
| 8685 | @@ -36,6 +36,7 @@ import java.util.Objects;
|
|---|
| 8686 | import java.util.Optional;
|
|---|
| 8687 | import java.util.concurrent.ForkJoinPool;
|
|---|
| 8688 | import java.util.concurrent.TimeUnit;
|
|---|
| 8689 | +import java.util.concurrent.locks.Lock;
|
|---|
| 8690 | import java.util.function.BiConsumer;
|
|---|
| 8691 | import java.util.function.Consumer;
|
|---|
| 8692 | import java.util.function.Supplier;
|
|---|
| 8693 | @@ -1637,13 +1638,13 @@ public class StyledMapRenderer extends AbstractMapRenderer {
|
|---|
| 8694 | RenderBenchmarkCollector benchmark = benchmarkFactory.get();
|
|---|
| 8695 | BBox bbox = bounds.toBBox();
|
|---|
| 8696 | getSettings(renderVirtualNodes);
|
|---|
| 8697 | -
|
|---|
| 8698 | try {
|
|---|
| 8699 | - if (data.getReadLock().tryLock(1, TimeUnit.SECONDS)) {
|
|---|
| 8700 | + Lock readLock = data.getReadLock();
|
|---|
| 8701 | + if (readLock.tryLock(1, TimeUnit.SECONDS)) {
|
|---|
| 8702 | try {
|
|---|
| 8703 | paintWithLock(data, renderVirtualNodes, benchmark, bbox);
|
|---|
| 8704 | } finally {
|
|---|
| 8705 | - data.getReadLock().unlock();
|
|---|
| 8706 | + readLock.unlock();
|
|---|
| 8707 | }
|
|---|
| 8708 | } else {
|
|---|
| 8709 | Logging.warn("Cannot paint layer {0}: It is locked.");
|
|---|
| 8710 | diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationNodeMap.java b/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationNodeMap.java
|
|---|
| 8711 | index 0ac990275..1b768e6fb 100644
|
|---|
| 8712 | --- a/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationNodeMap.java
|
|---|
| 8713 | +++ b/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationNodeMap.java
|
|---|
| 8714 | @@ -10,9 +10,10 @@ import java.util.Set;
|
|---|
| 8715 | import java.util.TreeMap;
|
|---|
| 8716 | import java.util.TreeSet;
|
|---|
| 8717 |
|
|---|
| 8718 | -import org.openstreetmap.josm.data.osm.Node;
|
|---|
| 8719 | -import org.openstreetmap.josm.data.osm.RelationMember;
|
|---|
| 8720 | -import org.openstreetmap.josm.data.osm.Way;
|
|---|
| 8721 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 8722 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 8723 | +import org.openstreetmap.josm.data.osm.IRelationMember;
|
|---|
| 8724 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 8725 |
|
|---|
| 8726 | /**
|
|---|
| 8727 | * Auxiliary class for relation sorting.
|
|---|
| 8728 | @@ -26,15 +27,16 @@ import org.openstreetmap.josm.data.osm.Way;
|
|---|
| 8729 | * (that are shared by other members).
|
|---|
| 8730 | *
|
|---|
| 8731 | * @author Christiaan Welvaart <cjw@time4t.net>
|
|---|
| 8732 | - * @since 1785
|
|---|
| 8733 | + * @param <T> The type of {@link IRelationMember}
|
|---|
| 8734 | + * @since 1785, xxx (generics)
|
|---|
| 8735 | */
|
|---|
| 8736 | -public class RelationNodeMap {
|
|---|
| 8737 | +public class RelationNodeMap<T extends IRelationMember<? extends IPrimitive>> {
|
|---|
| 8738 |
|
|---|
| 8739 | private static final String ROLE_BACKWARD = "backward";
|
|---|
| 8740 |
|
|---|
| 8741 | private static class NodesWays {
|
|---|
| 8742 | - public final Map<Node, Set<Integer>> nodes = new TreeMap<>();
|
|---|
| 8743 | - public final Map<Integer, Set<Node>> ways = new TreeMap<>();
|
|---|
| 8744 | + public final Map<INode, Set<Integer>> nodes = new TreeMap<>();
|
|---|
| 8745 | + public final Map<Integer, Set<INode>> ways = new TreeMap<>();
|
|---|
| 8746 | public final boolean oneWay;
|
|---|
| 8747 |
|
|---|
| 8748 | NodesWays(boolean oneWay) {
|
|---|
| 8749 | @@ -56,7 +58,7 @@ public class RelationNodeMap {
|
|---|
| 8750 | * Used to keep track of what members are done.
|
|---|
| 8751 | */
|
|---|
| 8752 | private final Set<Integer> remaining = new TreeSet<>();
|
|---|
| 8753 | - private final Map<Integer, Set<Node>> remainingOneway = new TreeMap<>();
|
|---|
| 8754 | + private final Map<Integer, Set<INode>> remainingOneway = new TreeMap<>();
|
|---|
| 8755 |
|
|---|
| 8756 | /**
|
|---|
| 8757 | * All members that are incomplete or not a way
|
|---|
| 8758 | @@ -67,8 +69,9 @@ public class RelationNodeMap {
|
|---|
| 8759 | * Gets the start node of the member, respecting the direction role.
|
|---|
| 8760 | * @param m The relation member.
|
|---|
| 8761 | * @return <code>null</code> if the member is no way, the node otherwise.
|
|---|
| 8762 | + * @since xxx (generics)
|
|---|
| 8763 | */
|
|---|
| 8764 | - public static Node firstOnewayNode(RelationMember m) {
|
|---|
| 8765 | + public static INode firstOnewayNode(IRelationMember<?> m) {
|
|---|
| 8766 | if (!m.isWay()) return null;
|
|---|
| 8767 | if (ROLE_BACKWARD.equals(m.getRole())) {
|
|---|
| 8768 | return m.getWay().lastNode();
|
|---|
| 8769 | @@ -81,7 +84,7 @@ public class RelationNodeMap {
|
|---|
| 8770 | * @param m The relation member.
|
|---|
| 8771 | * @return <code>null</code> if the member is no way, the node otherwise.
|
|---|
| 8772 | */
|
|---|
| 8773 | - public static Node lastOnewayNode(RelationMember m) {
|
|---|
| 8774 | + public static INode lastOnewayNode(IRelationMember<?> m) {
|
|---|
| 8775 | if (!m.isWay()) return null;
|
|---|
| 8776 | if (ROLE_BACKWARD.equals(m.getRole())) {
|
|---|
| 8777 | return m.getWay().firstNode();
|
|---|
| 8778 | @@ -89,17 +92,17 @@ public class RelationNodeMap {
|
|---|
| 8779 | return m.getWay().lastNode();
|
|---|
| 8780 | }
|
|---|
| 8781 |
|
|---|
| 8782 | - RelationNodeMap(List<RelationMember> members) {
|
|---|
| 8783 | + RelationNodeMap(List<T> members) {
|
|---|
| 8784 | for (int i = 0; i < members.size(); ++i) {
|
|---|
| 8785 | - RelationMember m = members.get(i);
|
|---|
| 8786 | + T m = members.get(i);
|
|---|
| 8787 | if (m.getMember().isIncomplete() || !m.isWay() || m.getWay().getNodesCount() < 2) {
|
|---|
| 8788 | notSortable.add(i);
|
|---|
| 8789 | continue;
|
|---|
| 8790 | }
|
|---|
| 8791 |
|
|---|
| 8792 | - Way w = m.getWay();
|
|---|
| 8793 | + IWay<?> w = m.getWay();
|
|---|
| 8794 | if (RelationSortUtils.roundaboutType(w) != NONE) {
|
|---|
| 8795 | - for (Node nd : w.getNodes()) {
|
|---|
| 8796 | + for (INode nd : w.getNodes()) {
|
|---|
| 8797 | addPair(nd, i);
|
|---|
| 8798 | }
|
|---|
| 8799 | } else if (RelationSortUtils.isOneway(m)) {
|
|---|
| 8800 | @@ -118,34 +121,34 @@ public class RelationNodeMap {
|
|---|
| 8801 | remaining.addAll(map.ways.keySet());
|
|---|
| 8802 | }
|
|---|
| 8803 |
|
|---|
| 8804 | - private void addPair(Node n, int i) {
|
|---|
| 8805 | + private void addPair(INode n, int i) {
|
|---|
| 8806 | map.nodes.computeIfAbsent(n, k -> new TreeSet<>()).add(i);
|
|---|
| 8807 | map.ways.computeIfAbsent(i, k -> new TreeSet<>()).add(n);
|
|---|
| 8808 | }
|
|---|
| 8809 |
|
|---|
| 8810 | - private void addNodeWayMap(Node n, int i) {
|
|---|
| 8811 | + private void addNodeWayMap(INode n, int i) {
|
|---|
| 8812 | onewayMap.nodes.computeIfAbsent(n, k -> new TreeSet<>()).add(i);
|
|---|
| 8813 | }
|
|---|
| 8814 |
|
|---|
| 8815 | - private void addWayNodeMap(Node n, int i) {
|
|---|
| 8816 | + private void addWayNodeMap(INode n, int i) {
|
|---|
| 8817 | onewayMap.ways.computeIfAbsent(i, k -> new TreeSet<>()).add(n);
|
|---|
| 8818 | }
|
|---|
| 8819 |
|
|---|
| 8820 | - private void addNodeWayMapReverse(Node n, int i) {
|
|---|
| 8821 | + private void addNodeWayMapReverse(INode n, int i) {
|
|---|
| 8822 | onewayReverseMap.nodes.computeIfAbsent(n, k -> new TreeSet<>()).add(i);
|
|---|
| 8823 | }
|
|---|
| 8824 |
|
|---|
| 8825 | - private void addWayNodeMapReverse(Node n, int i) {
|
|---|
| 8826 | + private void addWayNodeMapReverse(INode n, int i) {
|
|---|
| 8827 | onewayReverseMap.ways.computeIfAbsent(i, k -> new TreeSet<>()).add(n);
|
|---|
| 8828 | }
|
|---|
| 8829 |
|
|---|
| 8830 | - private void addRemainingForward(Node n, int i) {
|
|---|
| 8831 | + private void addRemainingForward(INode n, int i) {
|
|---|
| 8832 | remainingOneway.computeIfAbsent(i, k -> new TreeSet<>()).add(n);
|
|---|
| 8833 | }
|
|---|
| 8834 |
|
|---|
| 8835 | private Integer firstOneway;
|
|---|
| 8836 | - private Node lastOnewayNode;
|
|---|
| 8837 | - private Node firstCircular;
|
|---|
| 8838 | + private INode lastOnewayNode;
|
|---|
| 8839 | + private INode firstCircular;
|
|---|
| 8840 |
|
|---|
| 8841 | /**
|
|---|
| 8842 | * Return a relation member that is linked to the member 'i', but has not been popped yet.
|
|---|
| 8843 | @@ -158,7 +161,7 @@ public class RelationNodeMap {
|
|---|
| 8844 | if (firstOneway != null) return popForwardOnewayPart(way);
|
|---|
| 8845 |
|
|---|
| 8846 | if (map.ways.containsKey(way)) {
|
|---|
| 8847 | - for (Node n : map.ways.get(way)) {
|
|---|
| 8848 | + for (INode n : map.ways.get(way)) {
|
|---|
| 8849 | Integer i = deleteAndGetAdjacentNode(map, n);
|
|---|
| 8850 | if (i != null) return i;
|
|---|
| 8851 |
|
|---|
| 8852 | @@ -176,7 +179,7 @@ public class RelationNodeMap {
|
|---|
| 8853 |
|
|---|
| 8854 | private Integer popForwardOnewayPart(Integer way) {
|
|---|
| 8855 | if (onewayMap.ways.containsKey(way)) {
|
|---|
| 8856 | - Node exitNode = onewayMap.ways.get(way).iterator().next();
|
|---|
| 8857 | + INode exitNode = onewayMap.ways.get(way).iterator().next();
|
|---|
| 8858 |
|
|---|
| 8859 | if (checkIfEndOfLoopReached(exitNode)) {
|
|---|
| 8860 | lastOnewayNode = exitNode;
|
|---|
| 8861 | @@ -201,7 +204,7 @@ public class RelationNodeMap {
|
|---|
| 8862 | // Check if the given node can be the end of the loop (i.e. it has
|
|---|
| 8863 | // an outgoing bidirectional or multiple outgoing oneways, or we
|
|---|
| 8864 | // looped back to our first circular node)
|
|---|
| 8865 | - private boolean checkIfEndOfLoopReached(Node n) {
|
|---|
| 8866 | + private boolean checkIfEndOfLoopReached(INode n) {
|
|---|
| 8867 | return map.nodes.containsKey(n)
|
|---|
| 8868 | || (onewayMap.nodes.containsKey(n) && (onewayMap.nodes.get(n).size() > 1))
|
|---|
| 8869 | || ((firstCircular != null) && (firstCircular == n));
|
|---|
| 8870 | @@ -209,14 +212,14 @@ public class RelationNodeMap {
|
|---|
| 8871 |
|
|---|
| 8872 | private Integer popBackwardOnewayPart(int way) {
|
|---|
| 8873 | if (lastOnewayNode != null) {
|
|---|
| 8874 | - Set<Node> nodes = new TreeSet<>();
|
|---|
| 8875 | + Set<INode> nodes = new TreeSet<>();
|
|---|
| 8876 | if (onewayReverseMap.ways.containsKey(way)) {
|
|---|
| 8877 | nodes.addAll(onewayReverseMap.ways.get(way));
|
|---|
| 8878 | }
|
|---|
| 8879 | if (map.ways.containsKey(way)) {
|
|---|
| 8880 | nodes.addAll(map.ways.get(way));
|
|---|
| 8881 | }
|
|---|
| 8882 | - for (Node n : nodes) {
|
|---|
| 8883 | + for (INode n : nodes) {
|
|---|
| 8884 | if (n == lastOnewayNode) { //if oneway part ends
|
|---|
| 8885 | firstOneway = null;
|
|---|
| 8886 | lastOnewayNode = null;
|
|---|
| 8887 | @@ -247,20 +250,20 @@ public class RelationNodeMap {
|
|---|
| 8888 | * @param n node
|
|---|
| 8889 | * @return node next to n
|
|---|
| 8890 | */
|
|---|
| 8891 | - private Integer deleteAndGetAdjacentNode(NodesWays nw, Node n) {
|
|---|
| 8892 | + private Integer deleteAndGetAdjacentNode(NodesWays nw, INode n) {
|
|---|
| 8893 | Integer j = findAdjacentWay(nw, n);
|
|---|
| 8894 | if (j == null) return null;
|
|---|
| 8895 | deleteWayNode(nw, j, n);
|
|---|
| 8896 | return j;
|
|---|
| 8897 | }
|
|---|
| 8898 |
|
|---|
| 8899 | - private static Integer findAdjacentWay(NodesWays nw, Node n) {
|
|---|
| 8900 | + private static Integer findAdjacentWay(NodesWays nw, INode n) {
|
|---|
| 8901 | Set<Integer> adj = nw.nodes.get(n);
|
|---|
| 8902 | if (adj == null || adj.isEmpty()) return null;
|
|---|
| 8903 | return adj.iterator().next();
|
|---|
| 8904 | }
|
|---|
| 8905 |
|
|---|
| 8906 | - private void deleteWayNode(NodesWays nw, Integer way, Node n) {
|
|---|
| 8907 | + private void deleteWayNode(NodesWays nw, Integer way, INode n) {
|
|---|
| 8908 | if (nw.oneWay) {
|
|---|
| 8909 | doneOneway(way);
|
|---|
| 8910 | } else {
|
|---|
| 8911 | @@ -285,7 +288,7 @@ public class RelationNodeMap {
|
|---|
| 8912 |
|
|---|
| 8913 | if (remainingOneway.isEmpty()) return null;
|
|---|
| 8914 | for (Integer i : remainingOneway.keySet()) { //find oneway, which is connected to more than one way (is between two oneway loops)
|
|---|
| 8915 | - for (Node n : onewayReverseMap.ways.get(i)) {
|
|---|
| 8916 | + for (INode n : onewayReverseMap.ways.get(i)) {
|
|---|
| 8917 | if (onewayReverseMap.nodes.containsKey(n) && onewayReverseMap.nodes.get(n).size() > 1) {
|
|---|
| 8918 | doneOneway(i);
|
|---|
| 8919 | firstCircular = n;
|
|---|
| 8920 | @@ -305,8 +308,8 @@ public class RelationNodeMap {
|
|---|
| 8921 | * @param i member key
|
|---|
| 8922 | */
|
|---|
| 8923 | private void doneOneway(Integer i) {
|
|---|
| 8924 | - Set<Node> nodesForward = remainingOneway.get(i);
|
|---|
| 8925 | - for (Node n : nodesForward) {
|
|---|
| 8926 | + Set<INode> nodesForward = remainingOneway.get(i);
|
|---|
| 8927 | + for (INode n : nodesForward) {
|
|---|
| 8928 | if (onewayMap.nodes.containsKey(n)) {
|
|---|
| 8929 | onewayMap.nodes.get(n).remove(i);
|
|---|
| 8930 | }
|
|---|
| 8931 | @@ -319,8 +322,8 @@ public class RelationNodeMap {
|
|---|
| 8932 |
|
|---|
| 8933 | private void done(Integer i) {
|
|---|
| 8934 | remaining.remove(i);
|
|---|
| 8935 | - Set<Node> nodes = map.ways.get(i);
|
|---|
| 8936 | - for (Node n : nodes) {
|
|---|
| 8937 | + Set<INode> nodes = map.ways.get(i);
|
|---|
| 8938 | + for (INode n : nodes) {
|
|---|
| 8939 | boolean result = map.nodes.get(n).remove(i);
|
|---|
| 8940 | if (!result) throw new AssertionError();
|
|---|
| 8941 | }
|
|---|
| 8942 | diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSortUtils.java b/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSortUtils.java
|
|---|
| 8943 | index d7457f7f1..70023011d 100644
|
|---|
| 8944 | --- a/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSortUtils.java
|
|---|
| 8945 | +++ b/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSortUtils.java
|
|---|
| 8946 | @@ -6,9 +6,9 @@ import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType
|
|---|
| 8947 | import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.ROUNDABOUT_RIGHT;
|
|---|
| 8948 |
|
|---|
| 8949 | import org.openstreetmap.josm.data.coor.EastNorth;
|
|---|
| 8950 | -import org.openstreetmap.josm.data.osm.Node;
|
|---|
| 8951 | -import org.openstreetmap.josm.data.osm.RelationMember;
|
|---|
| 8952 | -import org.openstreetmap.josm.data.osm.Way;
|
|---|
| 8953 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 8954 | +import org.openstreetmap.josm.data.osm.IRelationMember;
|
|---|
| 8955 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 8956 | import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction;
|
|---|
| 8957 |
|
|---|
| 8958 | /**
|
|---|
| 8959 | @@ -24,19 +24,27 @@ final class RelationSortUtils {
|
|---|
| 8960 | * determine, if the way i is a roundabout and if yes, what type of roundabout
|
|---|
| 8961 | * @param member relation member
|
|---|
| 8962 | * @return roundabout type
|
|---|
| 8963 | + * @since xxx (generics)
|
|---|
| 8964 | */
|
|---|
| 8965 | - static Direction roundaboutType(RelationMember member) {
|
|---|
| 8966 | + static Direction roundaboutType(IRelationMember<?> member) {
|
|---|
| 8967 | if (member == null || !member.isWay()) return NONE;
|
|---|
| 8968 | - return roundaboutType(member.getWay());
|
|---|
| 8969 | + return roundaboutType((IWay<?>) member.getWay());
|
|---|
| 8970 | }
|
|---|
| 8971 |
|
|---|
| 8972 | - static Direction roundaboutType(Way w) {
|
|---|
| 8973 | + /**
|
|---|
| 8974 | + * Check if a way is a roundabout type
|
|---|
| 8975 | + * @param w The way to check
|
|---|
| 8976 | + * @param <W> The way type
|
|---|
| 8977 | + * @return The roundabout type
|
|---|
| 8978 | + * @since xxx (generics)
|
|---|
| 8979 | + */
|
|---|
| 8980 | + static <W extends IWay<?>> Direction roundaboutType(W w) {
|
|---|
| 8981 | if (w != null && w.hasTag("junction", "circular", "roundabout")) {
|
|---|
| 8982 | int nodesCount = w.getNodesCount();
|
|---|
| 8983 | if (nodesCount > 2 && nodesCount < 200) {
|
|---|
| 8984 | - Node n1 = w.getNode(0);
|
|---|
| 8985 | - Node n2 = w.getNode(1);
|
|---|
| 8986 | - Node n3 = w.getNode(2);
|
|---|
| 8987 | + INode n1 = w.getNode(0);
|
|---|
| 8988 | + INode n2 = w.getNode(1);
|
|---|
| 8989 | + INode n3 = w.getNode(2);
|
|---|
| 8990 | if (n1 != null && n2 != null && n3 != null && w.isClosed()) {
|
|---|
| 8991 | /** do some simple determinant / cross product test on the first 3 nodes
|
|---|
| 8992 | to see, if the roundabout goes clock wise or ccw */
|
|---|
| 8993 | @@ -54,15 +62,15 @@ final class RelationSortUtils {
|
|---|
| 8994 | return NONE;
|
|---|
| 8995 | }
|
|---|
| 8996 |
|
|---|
| 8997 | - static boolean isBackward(final RelationMember member) {
|
|---|
| 8998 | + static boolean isBackward(final IRelationMember<?> member) {
|
|---|
| 8999 | return "backward".equals(member.getRole());
|
|---|
| 9000 | }
|
|---|
| 9001 |
|
|---|
| 9002 | - static boolean isForward(final RelationMember member) {
|
|---|
| 9003 | + static boolean isForward(final IRelationMember<?> member) {
|
|---|
| 9004 | return "forward".equals(member.getRole());
|
|---|
| 9005 | }
|
|---|
| 9006 |
|
|---|
| 9007 | - static boolean isOneway(final RelationMember member) {
|
|---|
| 9008 | + static boolean isOneway(final IRelationMember<?> member) {
|
|---|
| 9009 | return isForward(member) || isBackward(member);
|
|---|
| 9010 | }
|
|---|
| 9011 | }
|
|---|
| 9012 | diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorter.java b/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorter.java
|
|---|
| 9013 | index 12713094a..34d6bdf4f 100644
|
|---|
| 9014 | --- a/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorter.java
|
|---|
| 9015 | +++ b/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorter.java
|
|---|
| 9016 | @@ -15,6 +15,8 @@ import java.util.Objects;
|
|---|
| 9017 | import java.util.stream.Collectors;
|
|---|
| 9018 |
|
|---|
| 9019 | import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
|
|---|
| 9020 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 9021 | +import org.openstreetmap.josm.data.osm.IRelationMember;
|
|---|
| 9022 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
|---|
| 9023 | import org.openstreetmap.josm.data.osm.Relation;
|
|---|
| 9024 | import org.openstreetmap.josm.data.osm.RelationMember;
|
|---|
| 9025 | @@ -194,12 +196,12 @@ public class RelationSorter {
|
|---|
| 9026 | * Sorts a list of members by connectivity
|
|---|
| 9027 | * @param defaultMembers The members to sort
|
|---|
| 9028 | * @return A sorted list of the same members
|
|---|
| 9029 | + * @since xxx (signature change, generics)
|
|---|
| 9030 | */
|
|---|
| 9031 | - public static List<RelationMember> sortMembersByConnectivity(List<RelationMember> defaultMembers) {
|
|---|
| 9032 | + public static <T extends IRelationMember<? extends IPrimitive>> List<T> sortMembersByConnectivity(List<T> defaultMembers) {
|
|---|
| 9033 | + List<T> newMembers;
|
|---|
| 9034 |
|
|---|
| 9035 | - List<RelationMember> newMembers;
|
|---|
| 9036 | -
|
|---|
| 9037 | - RelationNodeMap map = new RelationNodeMap(defaultMembers);
|
|---|
| 9038 | + RelationNodeMap<T> map = new RelationNodeMap<>(defaultMembers);
|
|---|
| 9039 | // List of groups of linked members
|
|---|
| 9040 | //
|
|---|
| 9041 | List<LinkedList<Integer>> allGroups = new ArrayList<>();
|
|---|
| 9042 | diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
|
|---|
| 9043 | index 4dcb27fd5..c7331faba 100644
|
|---|
| 9044 | --- a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
|
|---|
| 9045 | +++ b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
|
|---|
| 9046 | @@ -86,6 +86,7 @@ import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 9047 | import org.openstreetmap.josm.data.imagery.OffsetBookmark;
|
|---|
| 9048 | import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
|
|---|
| 9049 | import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
|
|---|
| 9050 | +import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 9051 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
|
|---|
| 9052 | import org.openstreetmap.josm.data.preferences.BooleanProperty;
|
|---|
| 9053 | import org.openstreetmap.josm.data.preferences.IntegerProperty;
|
|---|
| 9054 | @@ -109,6 +110,7 @@ import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings.FilterChan
|
|---|
| 9055 | import org.openstreetmap.josm.gui.layer.imagery.IncreaseZoomAction;
|
|---|
| 9056 | import org.openstreetmap.josm.gui.layer.imagery.LoadAllTilesAction;
|
|---|
| 9057 | import org.openstreetmap.josm.gui.layer.imagery.LoadErroneousTilesAction;
|
|---|
| 9058 | +import org.openstreetmap.josm.gui.layer.imagery.MVTLayer;
|
|---|
| 9059 | import org.openstreetmap.josm.gui.layer.imagery.ReprojectionTile;
|
|---|
| 9060 | import org.openstreetmap.josm.gui.layer.imagery.ShowErrorsAction;
|
|---|
| 9061 | import org.openstreetmap.josm.gui.layer.imagery.TileAnchor;
|
|---|
| 9062 | @@ -888,7 +890,7 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
|---|
| 9063 | if (coordinateConverter.requiresReprojection()) {
|
|---|
| 9064 | tile = new ReprojectionTile(tileSource, x, y, zoom);
|
|---|
| 9065 | } else {
|
|---|
| 9066 | - tile = new Tile(tileSource, x, y, zoom);
|
|---|
| 9067 | + tile = createTile(tileSource, x, y, zoom);
|
|---|
| 9068 | }
|
|---|
| 9069 | tileCache.addTile(tile);
|
|---|
| 9070 | }
|
|---|
| 9071 | @@ -1041,7 +1043,7 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
|---|
| 9072 | img = getLoadedTileImage(tile);
|
|---|
| 9073 | anchorImage = getAnchor(tile, img);
|
|---|
| 9074 | }
|
|---|
| 9075 | - if (img == null || anchorImage == null) {
|
|---|
| 9076 | + if (img == null || anchorImage == null || (tile instanceof VectorTile && !tile.isLoaded())) {
|
|---|
| 9077 | miss = true;
|
|---|
| 9078 | }
|
|---|
| 9079 | }
|
|---|
| 9080 | @@ -1050,7 +1052,9 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
|---|
| 9081 | return;
|
|---|
| 9082 | }
|
|---|
| 9083 |
|
|---|
| 9084 | - img = applyImageProcessors(img);
|
|---|
| 9085 | + if (img != null) {
|
|---|
| 9086 | + img = applyImageProcessors(img);
|
|---|
| 9087 | + }
|
|---|
| 9088 |
|
|---|
| 9089 | TileAnchor anchorScreen = coordinateConverter.getScreenAnchorForTile(tile);
|
|---|
| 9090 | synchronized (paintMutex) {
|
|---|
| 9091 | @@ -1862,7 +1866,7 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
|---|
| 9092 |
|
|---|
| 9093 | for (int x = minX; x <= maxX; x++) {
|
|---|
| 9094 | for (int y = minY; y <= maxY; y++) {
|
|---|
| 9095 | - requestedTiles.add(new Tile(tileSource, x, y, currentZoomLevel));
|
|---|
| 9096 | + requestedTiles.add(createTile(tileSource, x, y, currentZoomLevel));
|
|---|
| 9097 | }
|
|---|
| 9098 | }
|
|---|
| 9099 | }
|
|---|
| 9100 | @@ -1968,6 +1972,20 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
|---|
| 9101 | return SaveActionBase.createAndOpenSaveFileChooser(tr("Save WMS file"), WMSLayerImporter.FILE_FILTER);
|
|---|
| 9102 | }
|
|---|
| 9103 |
|
|---|
| 9104 | + /**
|
|---|
| 9105 | + * Create a new tile. Added to allow use of custom {@link Tile} objects.
|
|---|
| 9106 | + *
|
|---|
| 9107 | + * @param source Tile source
|
|---|
| 9108 | + * @param x X coordinate
|
|---|
| 9109 | + * @param y Y coordinate
|
|---|
| 9110 | + * @param zoom Zoom level
|
|---|
| 9111 | + * @return The new {@link Tile}
|
|---|
| 9112 | + * @since xxx
|
|---|
| 9113 | + */
|
|---|
| 9114 | + public Tile createTile(T source, int x, int y, int zoom) {
|
|---|
| 9115 | + return new Tile(source, x, y, zoom);
|
|---|
| 9116 | + }
|
|---|
| 9117 | +
|
|---|
| 9118 | @Override
|
|---|
| 9119 | public synchronized void destroy() {
|
|---|
| 9120 | super.destroy();
|
|---|
| 9121 | @@ -1988,6 +2006,10 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
|---|
| 9122 | allocateCacheMemory();
|
|---|
| 9123 | if (memory != null) {
|
|---|
| 9124 | doPaint(graphics);
|
|---|
| 9125 | + if (AbstractTileSourceLayer.this instanceof MVTLayer) {
|
|---|
| 9126 | + AbstractTileSourceLayer.this.paint(graphics.getDefaultGraphics(), graphics.getMapView(), graphics.getMapView()
|
|---|
| 9127 | + .getRealBounds());
|
|---|
| 9128 | + }
|
|---|
| 9129 | } else {
|
|---|
| 9130 | Graphics g = graphics.getDefaultGraphics();
|
|---|
| 9131 | Color oldColor = g.getColor();
|
|---|
| 9132 | diff --git a/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java b/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
|
|---|
| 9133 | index 6c902b92a..933933da3 100644
|
|---|
| 9134 | --- a/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
|
|---|
| 9135 | +++ b/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
|
|---|
| 9136 | @@ -37,6 +37,7 @@ import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 9137 | import org.openstreetmap.josm.gui.MapView;
|
|---|
| 9138 | import org.openstreetmap.josm.gui.MenuScroller;
|
|---|
| 9139 | import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
|
|---|
| 9140 | +import org.openstreetmap.josm.gui.layer.imagery.MVTLayer;
|
|---|
| 9141 | import org.openstreetmap.josm.gui.widgets.UrlLabel;
|
|---|
| 9142 | import org.openstreetmap.josm.tools.GBC;
|
|---|
| 9143 | import org.openstreetmap.josm.tools.ImageProcessor;
|
|---|
| 9144 | @@ -168,6 +169,8 @@ public abstract class ImageryLayer extends Layer {
|
|---|
| 9145 | case BING:
|
|---|
| 9146 | case SCANEX:
|
|---|
| 9147 | return new TMSLayer(info);
|
|---|
| 9148 | + case MVT:
|
|---|
| 9149 | + return new MVTLayer(info);
|
|---|
| 9150 | default:
|
|---|
| 9151 | throw new AssertionError(tr("Unsupported imagery type: {0}", info.getImageryType()));
|
|---|
| 9152 | }
|
|---|
| 9153 | diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 9154 | new file mode 100644
|
|---|
| 9155 | index 000000000..aa335f7b0
|
|---|
| 9156 | --- /dev/null
|
|---|
| 9157 | +++ b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 9158 | @@ -0,0 +1,278 @@
|
|---|
| 9159 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 9160 | +package org.openstreetmap.josm.gui.layer.imagery;
|
|---|
| 9161 | +
|
|---|
| 9162 | +import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 9163 | +
|
|---|
| 9164 | +import java.awt.Component;
|
|---|
| 9165 | +import java.awt.Graphics2D;
|
|---|
| 9166 | +import java.awt.event.ActionEvent;
|
|---|
| 9167 | +import java.util.ArrayList;
|
|---|
| 9168 | +import java.util.Arrays;
|
|---|
| 9169 | +import java.util.Collection;
|
|---|
| 9170 | +import java.util.Collections;
|
|---|
| 9171 | +import java.util.HashMap;
|
|---|
| 9172 | +import java.util.List;
|
|---|
| 9173 | +import java.util.Map;
|
|---|
| 9174 | +import java.util.Objects;
|
|---|
| 9175 | +import java.util.function.BooleanSupplier;
|
|---|
| 9176 | +import java.util.function.Consumer;
|
|---|
| 9177 | +import java.util.stream.Collectors;
|
|---|
| 9178 | +
|
|---|
| 9179 | +import javax.swing.AbstractAction;
|
|---|
| 9180 | +import javax.swing.Action;
|
|---|
| 9181 | +import javax.swing.JCheckBoxMenuItem;
|
|---|
| 9182 | +import javax.swing.JMenuItem;
|
|---|
| 9183 | +
|
|---|
| 9184 | +import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 9185 | +import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
|
|---|
| 9186 | +import org.openstreetmap.josm.data.Bounds;
|
|---|
| 9187 | +import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 9188 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 9189 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTFile;
|
|---|
| 9190 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 9191 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile.TileListener;
|
|---|
| 9192 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapBoxVectorCachedTileLoader;
|
|---|
| 9193 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorTileSource;
|
|---|
| 9194 | +import org.openstreetmap.josm.data.osm.DataSet;
|
|---|
| 9195 | +import org.openstreetmap.josm.data.osm.Node;
|
|---|
| 9196 | +import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
|---|
| 9197 | +import org.openstreetmap.josm.data.osm.Relation;
|
|---|
| 9198 | +import org.openstreetmap.josm.data.osm.RelationMember;
|
|---|
| 9199 | +import org.openstreetmap.josm.data.osm.Way;
|
|---|
| 9200 | +import org.openstreetmap.josm.data.osm.visitor.paint.AbstractMapRenderer;
|
|---|
| 9201 | +import org.openstreetmap.josm.data.osm.visitor.paint.MapRendererFactory;
|
|---|
| 9202 | +import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
|
|---|
| 9203 | +import org.openstreetmap.josm.data.vector.VectorDataSet;
|
|---|
| 9204 | +import org.openstreetmap.josm.data.vector.VectorNode;
|
|---|
| 9205 | +import org.openstreetmap.josm.data.vector.VectorPrimitive;
|
|---|
| 9206 | +import org.openstreetmap.josm.data.vector.VectorRelation;
|
|---|
| 9207 | +import org.openstreetmap.josm.data.vector.VectorWay;
|
|---|
| 9208 | +import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 9209 | +import org.openstreetmap.josm.gui.MapView;
|
|---|
| 9210 | +import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer;
|
|---|
| 9211 | +import org.openstreetmap.josm.gui.layer.LayerManager;
|
|---|
| 9212 | +import org.openstreetmap.josm.gui.layer.OsmDataLayer;
|
|---|
| 9213 | +import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 9214 | +import org.openstreetmap.josm.gui.mappaint.StyleSource;
|
|---|
| 9215 | +
|
|---|
| 9216 | +/**
|
|---|
| 9217 | + * A layer for MapBox Vector Tiles
|
|---|
| 9218 | + * @author Taylor Smock
|
|---|
| 9219 | + * @since xxx
|
|---|
| 9220 | + */
|
|---|
| 9221 | +public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSource> implements TileListener {
|
|---|
| 9222 | + private static final String CACHE_REGION_NAME = "MVT";
|
|---|
| 9223 | + // Just to avoid allocating a bunch of 0 length action arrays
|
|---|
| 9224 | + private static final Action[] EMPTY_ACTIONS = new Action[0];
|
|---|
| 9225 | + private final Map<String, Boolean> layerNames = new HashMap<>();
|
|---|
| 9226 | + private final VectorDataSet dataSet = new VectorDataSet();
|
|---|
| 9227 | +
|
|---|
| 9228 | + /**
|
|---|
| 9229 | + * Creates an instance of an MVT layer
|
|---|
| 9230 | + *
|
|---|
| 9231 | + * @param info ImageryInfo describing the layer
|
|---|
| 9232 | + */
|
|---|
| 9233 | + public MVTLayer(ImageryInfo info) {
|
|---|
| 9234 | + super(info);
|
|---|
| 9235 | + }
|
|---|
| 9236 | +
|
|---|
| 9237 | + @Override
|
|---|
| 9238 | + protected Class<? extends TileLoader> getTileLoaderClass() {
|
|---|
| 9239 | + return MapBoxVectorCachedTileLoader.class;
|
|---|
| 9240 | + }
|
|---|
| 9241 | +
|
|---|
| 9242 | + @Override
|
|---|
| 9243 | + protected String getCacheName() {
|
|---|
| 9244 | + return CACHE_REGION_NAME;
|
|---|
| 9245 | + }
|
|---|
| 9246 | +
|
|---|
| 9247 | + @Override
|
|---|
| 9248 | + public Collection<String> getNativeProjections() {
|
|---|
| 9249 | + // MapBox Vector Tiles <i>specifically</i> only support EPSG:3857
|
|---|
| 9250 | + // ("it is exclusively geared towards square pixel tiles in {link to EPSG:3857}").
|
|---|
| 9251 | + return Collections.singleton(MVTFile.DEFAULT_PROJECTION);
|
|---|
| 9252 | + }
|
|---|
| 9253 | +
|
|---|
| 9254 | + @Override
|
|---|
| 9255 | + public void paint(Graphics2D g, MapView mv, Bounds box) {
|
|---|
| 9256 | + this.dataSet.setZoom(this.getZoomLevel());
|
|---|
| 9257 | + AbstractMapRenderer painter = MapRendererFactory.getInstance().createActiveRenderer(g, mv, false);
|
|---|
| 9258 | + painter.enableSlowOperations(mv.getMapMover() == null || !mv.getMapMover().movementInProgress()
|
|---|
| 9259 | + || !OsmDataLayer.PROPERTY_HIDE_LABELS_WHILE_DRAGGING.get());
|
|---|
| 9260 | + // Set the painter to use our custom style sheet
|
|---|
| 9261 | + if (painter instanceof StyledMapRenderer && this.dataSet.getStyles() != null) {
|
|---|
| 9262 | + ((StyledMapRenderer) painter).setStyles(this.dataSet.getStyles());
|
|---|
| 9263 | + }
|
|---|
| 9264 | + painter.render(this.dataSet, false, box);
|
|---|
| 9265 | + }
|
|---|
| 9266 | +
|
|---|
| 9267 | + @Override
|
|---|
| 9268 | + protected MapboxVectorTileSource getTileSource() {
|
|---|
| 9269 | + MapboxVectorTileSource source = new MapboxVectorTileSource(this.info);
|
|---|
| 9270 | + this.info.setAttribution(source);
|
|---|
| 9271 | + if (source.getStyleSource() != null) {
|
|---|
| 9272 | + List<ElemStyles> styles = source.getStyleSource().getSources().entrySet().stream()
|
|---|
| 9273 | + .filter(entry -> entry.getKey() == null || entry.getKey().getUrls().contains(source.getBaseUrl()))
|
|---|
| 9274 | + .map(Map.Entry::getValue).collect(Collectors.toList());
|
|---|
| 9275 | + // load the style sources
|
|---|
| 9276 | + styles.stream().map(ElemStyles::getStyleSources).flatMap(Collection::stream).forEach(StyleSource::loadStyleSource);
|
|---|
| 9277 | + this.dataSet.setStyles(styles);
|
|---|
| 9278 | + this.setName(source.getName());
|
|---|
| 9279 | + }
|
|---|
| 9280 | + return source;
|
|---|
| 9281 | + }
|
|---|
| 9282 | +
|
|---|
| 9283 | + @Override
|
|---|
| 9284 | + public Tile createTile(MapboxVectorTileSource source, int x, int y, int zoom) {
|
|---|
| 9285 | + final MVTTile tile = new MVTTile(source, x, y, zoom);
|
|---|
| 9286 | + tile.addTileLoaderFinisher(this);
|
|---|
| 9287 | + return tile;
|
|---|
| 9288 | + }
|
|---|
| 9289 | +
|
|---|
| 9290 | + @Override
|
|---|
| 9291 | + public Action[] getMenuEntries() {
|
|---|
| 9292 | + ArrayList<Action> actions = new ArrayList<>(Arrays.asList(super.getMenuEntries()));
|
|---|
| 9293 | + // Add separator between Info and the layers
|
|---|
| 9294 | + actions.add(SeparatorLayerAction.INSTANCE);
|
|---|
| 9295 | + for (Map.Entry<String, Boolean> layerConfig : layerNames.entrySet()) {
|
|---|
| 9296 | + actions.add(new EnableLayerAction(layerConfig.getKey(), () -> layerNames.computeIfAbsent(layerConfig.getKey(), key -> true),
|
|---|
| 9297 | + layer -> {
|
|---|
| 9298 | + layerNames.compute(layer, (key, value) -> Boolean.FALSE.equals(value));
|
|---|
| 9299 | + this.invalidate();
|
|---|
| 9300 | + }));
|
|---|
| 9301 | + }
|
|---|
| 9302 | + // Add separator between layers and convert action
|
|---|
| 9303 | + actions.add(SeparatorLayerAction.INSTANCE);
|
|---|
| 9304 | + actions.add(new ConvertLayerAction(this));
|
|---|
| 9305 | + return actions.toArray(EMPTY_ACTIONS);
|
|---|
| 9306 | + }
|
|---|
| 9307 | +
|
|---|
| 9308 | + /**
|
|---|
| 9309 | + * Get the data set for this layer
|
|---|
| 9310 | + */
|
|---|
| 9311 | + public VectorDataSet getData() {
|
|---|
| 9312 | + return this.dataSet;
|
|---|
| 9313 | + }
|
|---|
| 9314 | +
|
|---|
| 9315 | + private static class ConvertLayerAction extends AbstractAction implements LayerAction {
|
|---|
| 9316 | + private final MVTLayer layer;
|
|---|
| 9317 | +
|
|---|
| 9318 | + ConvertLayerAction(MVTLayer layer) {
|
|---|
| 9319 | + this.layer = layer;
|
|---|
| 9320 | + }
|
|---|
| 9321 | +
|
|---|
| 9322 | + @Override
|
|---|
| 9323 | + public void actionPerformed(ActionEvent e) {
|
|---|
| 9324 | + LayerManager manager = MainApplication.getLayerManager();
|
|---|
| 9325 | + VectorDataSet dataSet = layer.getData();
|
|---|
| 9326 | + DataSet osmData = new DataSet();
|
|---|
| 9327 | + // Add nodes first, map is to ensure we can map new nodes to vector nodes
|
|---|
| 9328 | + Map<VectorNode, Node> nodeMap = new HashMap<>(dataSet.getNodes().size());
|
|---|
| 9329 | + for (VectorNode vectorNode : dataSet.getNodes()) {
|
|---|
| 9330 | + Node newNode = new Node(vectorNode.getCoor());
|
|---|
| 9331 | + if (vectorNode.isTagged()) {
|
|---|
| 9332 | + vectorNode.getInterestingTags().forEach(newNode::put);
|
|---|
| 9333 | + }
|
|---|
| 9334 | + nodeMap.put(vectorNode, newNode);
|
|---|
| 9335 | + }
|
|---|
| 9336 | + // Add ways next
|
|---|
| 9337 | + Map<VectorWay, Way> wayMap = new HashMap<>(dataSet.getWays().size());
|
|---|
| 9338 | + for (VectorWay vectorWay : dataSet.getWays()) {
|
|---|
| 9339 | + Way newWay = new Way();
|
|---|
| 9340 | + List<Node> nodes = vectorWay.getNodes().stream().map(nodeMap::get).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 9341 | + newWay.setNodes(nodes);
|
|---|
| 9342 | + if (vectorWay.isTagged()) {
|
|---|
| 9343 | + vectorWay.getInterestingTags().forEach(newWay::put);
|
|---|
| 9344 | + }
|
|---|
| 9345 | + wayMap.put(vectorWay, newWay);
|
|---|
| 9346 | + }
|
|---|
| 9347 | +
|
|---|
| 9348 | + // Finally, add Relations
|
|---|
| 9349 | + Map<VectorRelation, Relation> relationMap = new HashMap<>(dataSet.getRelations().size());
|
|---|
| 9350 | + for (VectorRelation vectorRelation : dataSet.getRelations()) {
|
|---|
| 9351 | + Relation relation = new Relation();
|
|---|
| 9352 | + if (vectorRelation.isTagged()) {
|
|---|
| 9353 | + vectorRelation.getInterestingTags().forEach(relation::put);
|
|---|
| 9354 | + }
|
|---|
| 9355 | + List<RelationMember> members = vectorRelation.getMembers().stream().map(member -> {
|
|---|
| 9356 | + final OsmPrimitive primitive;
|
|---|
| 9357 | + final VectorPrimitive vectorPrimitive = member.getMember();
|
|---|
| 9358 | + if (vectorPrimitive instanceof VectorNode) {
|
|---|
| 9359 | + primitive = nodeMap.get(vectorPrimitive);
|
|---|
| 9360 | + } else if (vectorPrimitive instanceof VectorWay) {
|
|---|
| 9361 | + primitive = wayMap.get(vectorPrimitive);
|
|---|
| 9362 | + } else if (vectorPrimitive instanceof VectorRelation) {
|
|---|
| 9363 | + // Hopefully, relations are encountered in order...
|
|---|
| 9364 | + primitive = relationMap.get(vectorPrimitive);
|
|---|
| 9365 | + } else {
|
|---|
| 9366 | + primitive = null;
|
|---|
| 9367 | + }
|
|---|
| 9368 | + if (primitive == null) return null;
|
|---|
| 9369 | + return new RelationMember(member.getRole(), primitive);
|
|---|
| 9370 | + }).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 9371 | + relation.setMembers(members);
|
|---|
| 9372 | + relationMap.put(vectorRelation, relation);
|
|---|
| 9373 | + }
|
|---|
| 9374 | + try {
|
|---|
| 9375 | + osmData.beginUpdate();
|
|---|
| 9376 | + nodeMap.values().forEach(osmData::addPrimitive);
|
|---|
| 9377 | + wayMap.values().forEach(osmData::addPrimitive);
|
|---|
| 9378 | + relationMap.values().forEach(osmData::addPrimitive);
|
|---|
| 9379 | + } finally {
|
|---|
| 9380 | + osmData.endUpdate();
|
|---|
| 9381 | + }
|
|---|
| 9382 | + manager.addLayer(new OsmDataLayer(osmData, this.layer.getName(), null));
|
|---|
| 9383 | + manager.removeLayer(this.layer);
|
|---|
| 9384 | + }
|
|---|
| 9385 | +
|
|---|
| 9386 | + @Override
|
|---|
| 9387 | + public boolean supportLayers(List<org.openstreetmap.josm.gui.layer.Layer> layers) {
|
|---|
| 9388 | + return layers.stream().allMatch(MVTLayer.class::isInstance);
|
|---|
| 9389 | + }
|
|---|
| 9390 | +
|
|---|
| 9391 | + @Override
|
|---|
| 9392 | + public Component createMenuComponent() {
|
|---|
| 9393 | + JMenuItem menuItem = new JMenuItem(tr("Convert to OSM Data"));
|
|---|
| 9394 | + menuItem.addActionListener(this);
|
|---|
| 9395 | + return menuItem;
|
|---|
| 9396 | + }
|
|---|
| 9397 | + }
|
|---|
| 9398 | +
|
|---|
| 9399 | + private static class EnableLayerAction extends AbstractAction implements LayerAction {
|
|---|
| 9400 | + private final String layer;
|
|---|
| 9401 | + private final Consumer<String> consumer;
|
|---|
| 9402 | + private final BooleanSupplier state;
|
|---|
| 9403 | +
|
|---|
| 9404 | + EnableLayerAction(String layer, BooleanSupplier state, Consumer<String> consumer) {
|
|---|
| 9405 | + super(tr("Toggle layer {0}", layer));
|
|---|
| 9406 | + this.layer = layer;
|
|---|
| 9407 | + this.consumer = consumer;
|
|---|
| 9408 | + this.state = state;
|
|---|
| 9409 | + }
|
|---|
| 9410 | +
|
|---|
| 9411 | + @Override
|
|---|
| 9412 | + public void actionPerformed(ActionEvent e) {
|
|---|
| 9413 | + consumer.accept(layer);
|
|---|
| 9414 | + }
|
|---|
| 9415 | +
|
|---|
| 9416 | + @Override
|
|---|
| 9417 | + public boolean supportLayers(List<org.openstreetmap.josm.gui.layer.Layer> layers) {
|
|---|
| 9418 | + return layers.stream().allMatch(MVTLayer.class::isInstance);
|
|---|
| 9419 | + }
|
|---|
| 9420 | +
|
|---|
| 9421 | + @Override
|
|---|
| 9422 | + public Component createMenuComponent() {
|
|---|
| 9423 | + JCheckBoxMenuItem item = new JCheckBoxMenuItem(this);
|
|---|
| 9424 | + item.setSelected(this.state.getAsBoolean());
|
|---|
| 9425 | + return item;
|
|---|
| 9426 | + }
|
|---|
| 9427 | + }
|
|---|
| 9428 | +
|
|---|
| 9429 | + @Override
|
|---|
| 9430 | + public void finishedLoading(MVTTile tile) {
|
|---|
| 9431 | + for (Layer layer : tile.getLayers()) {
|
|---|
| 9432 | + this.layerNames.putIfAbsent(layer.getName(), true);
|
|---|
| 9433 | + }
|
|---|
| 9434 | + this.dataSet.addTileData(tile);
|
|---|
| 9435 | + }
|
|---|
| 9436 | +}
|
|---|
| 9437 | diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java
|
|---|
| 9438 | index d195742d8..a97573ecc 100644
|
|---|
| 9439 | --- a/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java
|
|---|
| 9440 | +++ b/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java
|
|---|
| 9441 | @@ -870,6 +870,17 @@ public final class ConditionFactory {
|
|---|
| 9442 | }
|
|---|
| 9443 | return e.osm.isSelected();
|
|---|
| 9444 | }
|
|---|
| 9445 | +
|
|---|
| 9446 | + /**
|
|---|
| 9447 | + * Check if the object is highlighted (i.e., is hovered over)
|
|---|
| 9448 | + * @param e The MapCSS environment
|
|---|
| 9449 | + * @return {@code true} if the object is highlighted
|
|---|
| 9450 | + * @see IPrimitive#isHighlighted
|
|---|
| 9451 | + * @since xxx
|
|---|
| 9452 | + */
|
|---|
| 9453 | + static boolean highlighted(Environment e) { // NO_UCD (unused code)
|
|---|
| 9454 | + return e.osm.isHighlighted();
|
|---|
| 9455 | + }
|
|---|
| 9456 | }
|
|---|
| 9457 |
|
|---|
| 9458 | /**
|
|---|
| 9459 | diff --git a/src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java b/src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java
|
|---|
| 9460 | new file mode 100644
|
|---|
| 9461 | index 000000000..99bbd058d
|
|---|
| 9462 | --- /dev/null
|
|---|
| 9463 | +++ b/src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java
|
|---|
| 9464 | @@ -0,0 +1,94 @@
|
|---|
| 9465 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 9466 | +package org.openstreetmap.josm.gui.preferences.imagery;
|
|---|
| 9467 | +
|
|---|
| 9468 | +import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 9469 | +
|
|---|
| 9470 | +import java.awt.event.KeyAdapter;
|
|---|
| 9471 | +import java.awt.event.KeyEvent;
|
|---|
| 9472 | +import java.util.Arrays;
|
|---|
| 9473 | +
|
|---|
| 9474 | +import javax.swing.JLabel;
|
|---|
| 9475 | +
|
|---|
| 9476 | +import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 9477 | +import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
|
|---|
| 9478 | +import org.openstreetmap.josm.gui.widgets.JosmTextArea;
|
|---|
| 9479 | +import org.openstreetmap.josm.gui.widgets.JosmTextField;
|
|---|
| 9480 | +import org.openstreetmap.josm.tools.GBC;
|
|---|
| 9481 | +import org.openstreetmap.josm.tools.Utils;
|
|---|
| 9482 | +
|
|---|
| 9483 | +/**
|
|---|
| 9484 | + * A panel for adding MapBox Vector Tile layers
|
|---|
| 9485 | + * @author Taylor Smock
|
|---|
| 9486 | + * @since xxx
|
|---|
| 9487 | + */
|
|---|
| 9488 | +public class AddMVTLayerPanel extends AddImageryPanel {
|
|---|
| 9489 | + private final JosmTextField mvtZoom = new JosmTextField();
|
|---|
| 9490 | + private final JosmTextArea mvtUrl = new JosmTextArea(3, 40).transferFocusOnTab();
|
|---|
| 9491 | +
|
|---|
| 9492 | + /**
|
|---|
| 9493 | + * Constructs a new {@code AddMVTLayerPanel}.
|
|---|
| 9494 | + */
|
|---|
| 9495 | + public AddMVTLayerPanel() {
|
|---|
| 9496 | +
|
|---|
| 9497 | + add(new JLabel(tr("{0} Make sure OSM has the permission to use this service", "1.")), GBC.eol());
|
|---|
| 9498 | + add(new JLabel(tr("{0} Enter URL (may be a style sheet url)", "2.")), GBC.eol());
|
|---|
| 9499 | + add(new JLabel("<html>" + Utils.joinAsHtmlUnorderedList(Arrays.asList(
|
|---|
| 9500 | + tr("{0} is replaced by tile zoom level, also supported:<br>" +
|
|---|
| 9501 | + "offsets to the zoom level: {1} or {2}<br>" +
|
|---|
| 9502 | + "reversed zoom level: {3}", "{zoom}", "{zoom+1}", "{zoom-1}", "{19-zoom}"),
|
|---|
| 9503 | + tr("{0} is replaced by X-coordinate of the tile", "{x}"),
|
|---|
| 9504 | + tr("{0} is replaced by Y-coordinate of the tile", "{y}"),
|
|---|
| 9505 | + tr("{0} is replaced by a random selection from the given comma separated list, e.g. {1}", "{switch:...}", "{switch:a,b,c}")
|
|---|
| 9506 | + )) + "</html>"), GBC.eol().fill());
|
|---|
| 9507 | +
|
|---|
| 9508 | + final KeyAdapter keyAdapter = new KeyAdapter() {
|
|---|
| 9509 | + @Override
|
|---|
| 9510 | + public void keyReleased(KeyEvent e) {
|
|---|
| 9511 | + mvtUrl.setText(buildMvtUrl());
|
|---|
| 9512 | + }
|
|---|
| 9513 | + };
|
|---|
| 9514 | +
|
|---|
| 9515 | + add(rawUrl, GBC.eop().fill());
|
|---|
| 9516 | + rawUrl.setLineWrap(true);
|
|---|
| 9517 | + rawUrl.addKeyListener(keyAdapter);
|
|---|
| 9518 | +
|
|---|
| 9519 | + add(new JLabel(tr("{0} Enter maximum zoom (optional)", "3.")), GBC.eol());
|
|---|
| 9520 | + mvtZoom.addKeyListener(keyAdapter);
|
|---|
| 9521 | + add(mvtZoom, GBC.eop().fill());
|
|---|
| 9522 | +
|
|---|
| 9523 | + add(new JLabel(tr("{0} Edit generated {1} URL (optional)", "4.", "MVT")), GBC.eol());
|
|---|
| 9524 | + add(mvtUrl, GBC.eop().fill());
|
|---|
| 9525 | + mvtUrl.setLineWrap(true);
|
|---|
| 9526 | +
|
|---|
| 9527 | + add(new JLabel(tr("{0} Enter name for this layer", "5.")), GBC.eol());
|
|---|
| 9528 | + add(name, GBC.eop().fill());
|
|---|
| 9529 | +
|
|---|
| 9530 | + registerValidableComponent(mvtUrl);
|
|---|
| 9531 | + }
|
|---|
| 9532 | +
|
|---|
| 9533 | + private String buildMvtUrl() {
|
|---|
| 9534 | + StringBuilder a = new StringBuilder("mvt");
|
|---|
| 9535 | + String z = sanitize(mvtZoom.getText());
|
|---|
| 9536 | + if (!z.isEmpty()) {
|
|---|
| 9537 | + a.append('[').append(z).append(']');
|
|---|
| 9538 | + }
|
|---|
| 9539 | + a.append(':').append(sanitize(getImageryRawUrl(), ImageryType.MVT));
|
|---|
| 9540 | + return a.toString();
|
|---|
| 9541 | + }
|
|---|
| 9542 | +
|
|---|
| 9543 | + @Override
|
|---|
| 9544 | + public ImageryInfo getImageryInfo() {
|
|---|
| 9545 | + final ImageryInfo generated = new ImageryInfo(getImageryName(), getMvtUrl());
|
|---|
| 9546 | + generated.setImageryType(ImageryType.MVT);
|
|---|
| 9547 | + return generated;
|
|---|
| 9548 | + }
|
|---|
| 9549 | +
|
|---|
| 9550 | + protected final String getMvtUrl() {
|
|---|
| 9551 | + return sanitize(mvtUrl.getText());
|
|---|
| 9552 | + }
|
|---|
| 9553 | +
|
|---|
| 9554 | + @Override
|
|---|
| 9555 | + protected boolean isImageryValid() {
|
|---|
| 9556 | + return !getImageryName().isEmpty() && !getMvtUrl().isEmpty();
|
|---|
| 9557 | + }
|
|---|
| 9558 | +}
|
|---|
| 9559 | diff --git a/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryProvidersPanel.java b/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryProvidersPanel.java
|
|---|
| 9560 | index d1d0ed096..cb78f9bda 100644
|
|---|
| 9561 | --- a/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryProvidersPanel.java
|
|---|
| 9562 | +++ b/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryProvidersPanel.java
|
|---|
| 9563 | @@ -312,6 +312,7 @@ public class ImageryProvidersPanel extends JPanel {
|
|---|
| 9564 | activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMS));
|
|---|
| 9565 | activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.TMS));
|
|---|
| 9566 | activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMTS));
|
|---|
| 9567 | + activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.MVT));
|
|---|
| 9568 | activeToolbar.add(remove);
|
|---|
| 9569 | activePanel.add(activeToolbar, BorderLayout.EAST);
|
|---|
| 9570 | add(activePanel, GBC.eol().fill(GridBagConstraints.BOTH).weight(2.0, 0.4).insets(5, 0, 0, 5));
|
|---|
| 9571 | @@ -440,6 +441,9 @@ public class ImageryProvidersPanel extends JPanel {
|
|---|
| 9572 | case WMTS:
|
|---|
| 9573 | icon = /* ICON(dialogs/) */ "add_wmts";
|
|---|
| 9574 | break;
|
|---|
| 9575 | + case MVT:
|
|---|
| 9576 | + icon = /* ICON(dialogs/) */ "add_mvt";
|
|---|
| 9577 | + break;
|
|---|
| 9578 | default:
|
|---|
| 9579 | break;
|
|---|
| 9580 | }
|
|---|
| 9581 | @@ -460,6 +464,9 @@ public class ImageryProvidersPanel extends JPanel {
|
|---|
| 9582 | case WMTS:
|
|---|
| 9583 | p = new AddWMTSLayerPanel();
|
|---|
| 9584 | break;
|
|---|
| 9585 | + case MVT:
|
|---|
| 9586 | + p = new AddMVTLayerPanel();
|
|---|
| 9587 | + break;
|
|---|
| 9588 | default:
|
|---|
| 9589 | throw new IllegalStateException("Type " + type + " not supported");
|
|---|
| 9590 | }
|
|---|
| 9591 | @@ -741,7 +748,7 @@ public class ImageryProvidersPanel extends JPanel {
|
|---|
| 9592 | private static boolean confirmEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) {
|
|---|
| 9593 | URL url;
|
|---|
| 9594 | try {
|
|---|
| 9595 | - url = new URL(eulaUrl.replaceAll("\\{lang\\}", LanguageInfo.getWikiLanguagePrefix()));
|
|---|
| 9596 | + url = new URL(eulaUrl.replaceAll("\\{lang}", LanguageInfo.getWikiLanguagePrefix()));
|
|---|
| 9597 | JosmEditorPane htmlPane;
|
|---|
| 9598 | try {
|
|---|
| 9599 | htmlPane = new JosmEditorPane(url);
|
|---|
| 9600 | @@ -749,7 +756,7 @@ public class ImageryProvidersPanel extends JPanel {
|
|---|
| 9601 | Logging.trace(e1);
|
|---|
| 9602 | // give a second chance with a default Locale 'en'
|
|---|
| 9603 | try {
|
|---|
| 9604 | - url = new URL(eulaUrl.replaceAll("\\{lang\\}", ""));
|
|---|
| 9605 | + url = new URL(eulaUrl.replaceAll("\\{lang}", ""));
|
|---|
| 9606 | htmlPane = new JosmEditorPane(url);
|
|---|
| 9607 | } catch (IOException e2) {
|
|---|
| 9608 | Logging.debug(e2);
|
|---|
| 9609 | --
|
|---|
| 9610 | GitLab
|
|---|
| 9611 |
|
|---|
| 9612 |
|
|---|
| 9613 | From 1f1b2e09776ce71c88845240a32d6532b8b58c56 Mon Sep 17 00:00:00 2001
|
|---|
| 9614 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 9615 | Date: Fri, 16 Apr 2021 06:25:45 -0600
|
|---|
| 9616 | Subject: [PATCH 08/50] MVT: Update maps.xsd for mvt support
|
|---|
| 9617 |
|
|---|
| 9618 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 9619 | ---
|
|---|
| 9620 | resources/data/maps.xsd | 3 ++-
|
|---|
| 9621 | 1 file changed, 2 insertions(+), 1 deletion(-)
|
|---|
| 9622 |
|
|---|
| 9623 | diff --git a/resources/data/maps.xsd b/resources/data/maps.xsd
|
|---|
| 9624 | index 0c7022370..ce5a1e88b 100644
|
|---|
| 9625 | --- a/resources/data/maps.xsd
|
|---|
| 9626 | +++ b/resources/data/maps.xsd
|
|---|
| 9627 | @@ -38,6 +38,7 @@
|
|---|
| 9628 | <xs:enumeration value="wms_endpoint" />
|
|---|
| 9629 | <xs:enumeration value="wmts" />
|
|---|
| 9630 | <xs:enumeration value="tms" />
|
|---|
| 9631 | + <xs:enumeration value="mvt" />
|
|---|
| 9632 | <xs:enumeration value="bing" />
|
|---|
| 9633 | <xs:enumeration value="scanex" />
|
|---|
| 9634 | </xs:restriction>
|
|---|
| 9635 | @@ -647,7 +648,7 @@
|
|---|
| 9636 | <xs:element name="id" minOccurs="1" maxOccurs="1" type="tns:id" />
|
|---|
| 9637 | <!-- Historic id for the imagery source -->
|
|---|
| 9638 | <xs:element name="oldid" minOccurs="0" maxOccurs="unbounded" type="tns:oldid" />
|
|---|
| 9639 | - <!-- The type. Can be tms, wms and html. In addition, there are the special types bing and scanex
|
|---|
| 9640 | + <!-- The type. Can be mvt, tms, wms and html. In addition, there are the special types bing and scanex
|
|---|
| 9641 | with hardcoded behaviour. -->
|
|---|
| 9642 | <xs:element name="type" minOccurs="1" maxOccurs="1" type="tns:type" />
|
|---|
| 9643 | <!-- To define as default server for this type -->
|
|---|
| 9644 | --
|
|---|
| 9645 | GitLab
|
|---|
| 9646 |
|
|---|
| 9647 |
|
|---|
| 9648 | From 0c314b387c48828fe7c3e90bb29be80de74585c2 Mon Sep 17 00:00:00 2001
|
|---|
| 9649 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 9650 | Date: Fri, 16 Apr 2021 07:03:43 -0600
|
|---|
| 9651 | Subject: [PATCH 09/50] MVT: Catch known exception
|
|---|
| 9652 |
|
|---|
| 9653 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 9654 | ---
|
|---|
| 9655 | .../mapbox/InvalidMapboxVectorTileException.java | 2 +-
|
|---|
| 9656 | .../vectortile/mapbox/style/MapBoxVectorStyle.java | 14 +++++++++++++-
|
|---|
| 9657 | .../imagery/vectortile/mapbox/style/Source.java | 3 ++-
|
|---|
| 9658 | 3 files changed, 16 insertions(+), 3 deletions(-)
|
|---|
| 9659 |
|
|---|
| 9660 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java
|
|---|
| 9661 | index d1186ad3f..bd47fe65f 100644
|
|---|
| 9662 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java
|
|---|
| 9663 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/InvalidMapboxVectorTileException.java
|
|---|
| 9664 | @@ -16,7 +16,7 @@ public class InvalidMapboxVectorTileException extends RuntimeException {
|
|---|
| 9665 | }
|
|---|
| 9666 |
|
|---|
| 9667 | /**
|
|---|
| 9668 | - * Create a new {@link InvalidMapboxVectorTile} exception with a message
|
|---|
| 9669 | + * Create a new {@link InvalidMapboxVectorTileException} exception with a message
|
|---|
| 9670 | * @param message The message
|
|---|
| 9671 | */
|
|---|
| 9672 | public InvalidMapboxVectorTileException(final String message) {
|
|---|
| 9673 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 9674 | index 746913042..ec24ee5cf 100644
|
|---|
| 9675 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 9676 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 9677 | @@ -29,6 +29,7 @@ import javax.json.JsonReader;
|
|---|
| 9678 | import javax.json.JsonStructure;
|
|---|
| 9679 | import javax.json.JsonValue;
|
|---|
| 9680 |
|
|---|
| 9681 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.InvalidMapboxVectorTileException;
|
|---|
| 9682 | import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
|
|---|
| 9683 | import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 9684 | import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 9685 | @@ -100,7 +101,18 @@ public class MapBoxVectorStyle {
|
|---|
| 9686 | if (jsonObject.containsKey("sources") && jsonObject.get("sources").getValueType() == JsonValue.ValueType.OBJECT) {
|
|---|
| 9687 | final JsonObject sourceObj = jsonObject.getJsonObject("sources");
|
|---|
| 9688 | sourceList = sourceObj.entrySet().stream().filter(entry -> entry.getValue().getValueType() == JsonValue.ValueType.OBJECT)
|
|---|
| 9689 | - .map(entry -> new Source(entry.getKey(), entry.getValue().asJsonObject())).collect(Collectors.toList());
|
|---|
| 9690 | + .map(entry -> {
|
|---|
| 9691 | + try {
|
|---|
| 9692 | + return new Source(entry.getKey(), entry.getValue().asJsonObject());
|
|---|
| 9693 | + } catch (InvalidMapboxVectorTileException e) {
|
|---|
| 9694 | + Logging.error(e);
|
|---|
| 9695 | + // Reraise if not a known exception
|
|---|
| 9696 | + if (!"TileJson not yet supported".equals(e.getMessage())) {
|
|---|
| 9697 | + throw e;
|
|---|
| 9698 | + }
|
|---|
| 9699 | + }
|
|---|
| 9700 | + return null;
|
|---|
| 9701 | + }).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 9702 | } else {
|
|---|
| 9703 | sourceList = Collections.emptyList();
|
|---|
| 9704 | }
|
|---|
| 9705 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 9706 | index dc7c62d62..dd41da72f 100644
|
|---|
| 9707 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 9708 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 9709 | @@ -17,6 +17,7 @@ import javax.json.JsonString;
|
|---|
| 9710 | import javax.json.JsonValue;
|
|---|
| 9711 |
|
|---|
| 9712 | import org.openstreetmap.josm.data.Bounds;
|
|---|
| 9713 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.InvalidMapboxVectorTileException;
|
|---|
| 9714 |
|
|---|
| 9715 | /**
|
|---|
| 9716 | * A source from a MapBox Vector Style
|
|---|
| 9717 | @@ -128,7 +129,7 @@ public class Source {
|
|---|
| 9718 | if (SourceType.VECTOR == this.sourceType || SourceType.RASTER == this.sourceType) {
|
|---|
| 9719 | if (data.containsKey("url")) {
|
|---|
| 9720 | // TODO implement https://github.com/mapbox/tilejson-spec
|
|---|
| 9721 | - throw new UnsupportedOperationException();
|
|---|
| 9722 | + throw new InvalidMapboxVectorTileException("TileJson not yet supported");
|
|---|
| 9723 | } else {
|
|---|
| 9724 | this.minZoom = ZOOM_BOUND_FUNCTION.apply(data.getInt("minzoom", 0));
|
|---|
| 9725 | this.maxZoom = ZOOM_BOUND_FUNCTION.apply(data.getInt("maxzoom", 22));
|
|---|
| 9726 | --
|
|---|
| 9727 | GitLab
|
|---|
| 9728 |
|
|---|
| 9729 |
|
|---|
| 9730 | From 80afe5c40c8c9d9a99b9b336b99f769a8bd09b8e Mon Sep 17 00:00:00 2001
|
|---|
| 9731 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 9732 | Date: Fri, 16 Apr 2021 07:10:18 -0600
|
|---|
| 9733 | Subject: [PATCH 10/50] Update highlighted mapcss stanza for modified mapcss
|
|---|
| 9734 | function registration
|
|---|
| 9735 |
|
|---|
| 9736 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 9737 | ---
|
|---|
| 9738 | .../josm/gui/mappaint/mapcss/ConditionFactory.java | 3 ++-
|
|---|
| 9739 | 1 file changed, 2 insertions(+), 1 deletion(-)
|
|---|
| 9740 |
|
|---|
| 9741 | diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java
|
|---|
| 9742 | index a97573ecc..becc3f0a8 100644
|
|---|
| 9743 | --- a/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java
|
|---|
| 9744 | +++ b/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java
|
|---|
| 9745 | @@ -878,7 +878,7 @@ public final class ConditionFactory {
|
|---|
| 9746 | * @see IPrimitive#isHighlighted
|
|---|
| 9747 | * @since xxx
|
|---|
| 9748 | */
|
|---|
| 9749 | - static boolean highlighted(Environment e) { // NO_UCD (unused code)
|
|---|
| 9750 | + static boolean highlighted(Environment e) {
|
|---|
| 9751 | return e.osm.isHighlighted();
|
|---|
| 9752 | }
|
|---|
| 9753 | }
|
|---|
| 9754 | @@ -898,6 +898,7 @@ public final class ConditionFactory {
|
|---|
| 9755 | PseudoClassCondition.register("closed2", PseudoClasses::closed2);
|
|---|
| 9756 | PseudoClassCondition.register("completely_downloaded", PseudoClasses::completely_downloaded);
|
|---|
| 9757 | PseudoClassCondition.register("connection", PseudoClasses::connection);
|
|---|
| 9758 | + PseudoClassCondition.register("highlighted", PseudoClasses::highlighted);
|
|---|
| 9759 | PseudoClassCondition.register("inDownloadedArea", PseudoClasses::inDownloadedArea);
|
|---|
| 9760 | PseudoClassCondition.register("modified", PseudoClasses::modified);
|
|---|
| 9761 | PseudoClassCondition.register("new", PseudoClasses::_new);
|
|---|
| 9762 | --
|
|---|
| 9763 | GitLab
|
|---|
| 9764 |
|
|---|
| 9765 |
|
|---|
| 9766 | From 729060323f9bceececcec48ddf9eb45f5c3cdeff Mon Sep 17 00:00:00 2001
|
|---|
| 9767 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 9768 | Date: Wed, 21 Apr 2021 16:23:57 -0600
|
|---|
| 9769 | Subject: [PATCH 11/50] Fix dropping features with dupe ids for MVTv1
|
|---|
| 9770 |
|
|---|
| 9771 | Also, make some performance modifications (hopefully)
|
|---|
| 9772 |
|
|---|
| 9773 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 9774 | ---
|
|---|
| 9775 | .../josm/data/vector/DataStore.java | 13 +-
|
|---|
| 9776 | .../josm/data/vector/VectorDataSet.java | 2 +-
|
|---|
| 9777 | .../josm/data/vector/VectorDataStore.java | 111 +++++++++++-------
|
|---|
| 9778 | .../josm/data/vector/VectorDataSetTest.java | 9 +-
|
|---|
| 9779 | 4 files changed, 88 insertions(+), 47 deletions(-)
|
|---|
| 9780 |
|
|---|
| 9781 | diff --git a/src/org/openstreetmap/josm/data/vector/DataStore.java b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 9782 | index 9de044f62..5175a534b 100644
|
|---|
| 9783 | --- a/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 9784 | +++ b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 9785 | @@ -20,6 +20,7 @@ import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 9786 | import org.openstreetmap.josm.data.osm.PrimitiveId;
|
|---|
| 9787 | import org.openstreetmap.josm.data.osm.QuadBucketPrimitiveStore;
|
|---|
| 9788 | import org.openstreetmap.josm.data.osm.Storage;
|
|---|
| 9789 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 9790 |
|
|---|
| 9791 | /**
|
|---|
| 9792 | * A class that stores data (essentially a simple {@link DataSet})
|
|---|
| 9793 | @@ -46,6 +47,7 @@ class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R exte
|
|---|
| 9794 | protected final int zoom;
|
|---|
| 9795 | protected final LocalQuadBucketPrimitiveStore<N, W, R> store = new LocalQuadBucketPrimitiveStore<>();
|
|---|
| 9796 | protected final Storage<O> allPrimitives = new Storage<>(new Storage.PrimitiveIdHash(), true);
|
|---|
| 9797 | + // TODO what happens when I use hashCode?
|
|---|
| 9798 | protected final Set<Tile> addedTiles = new HashSet<>();
|
|---|
| 9799 | protected final Map<PrimitiveId, O> primitivesMap = allPrimitives
|
|---|
| 9800 | .foreignKey(new Storage.PrimitiveIdHash());
|
|---|
| 9801 | @@ -96,13 +98,20 @@ class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R exte
|
|---|
| 9802 | if (primitive == null) {
|
|---|
| 9803 | return;
|
|---|
| 9804 | }
|
|---|
| 9805 | - // This is deliberate -- attempting to remove the primitive twice causes issues
|
|---|
| 9806 | - synchronized (primitive) {
|
|---|
| 9807 | + try {
|
|---|
| 9808 | + this.readWriteLock.writeLock().lockInterruptibly();
|
|---|
| 9809 | if (this.allPrimitives.contains(primitive)) {
|
|---|
| 9810 | this.store.removePrimitive(primitive);
|
|---|
| 9811 | this.allPrimitives.remove(primitive);
|
|---|
| 9812 | this.primitivesMap.remove(primitive.getPrimitiveId());
|
|---|
| 9813 | }
|
|---|
| 9814 | + } catch (InterruptedException e) {
|
|---|
| 9815 | + Logging.error(e);
|
|---|
| 9816 | + Thread.currentThread().interrupt();
|
|---|
| 9817 | + } finally {
|
|---|
| 9818 | + if (this.readWriteLock.isWriteLockedByCurrentThread()) {
|
|---|
| 9819 | + this.readWriteLock.writeLock().unlock();
|
|---|
| 9820 | + }
|
|---|
| 9821 | }
|
|---|
| 9822 | }
|
|---|
| 9823 |
|
|---|
| 9824 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 9825 | index dfa9334a3..8e18dae25 100644
|
|---|
| 9826 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 9827 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 9828 | @@ -474,7 +474,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 9829 | final int currentZoom = tile.getZoom();
|
|---|
| 9830 | // computeIfAbsent should be thread safe (ConcurrentHashMap indicates it is, anyway)
|
|---|
| 9831 | final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 9832 | - tryWrite(dataStore, () -> dataStore.addTile(tile));
|
|---|
| 9833 | + dataStore.addTile(tile);
|
|---|
| 9834 | }
|
|---|
| 9835 |
|
|---|
| 9836 | /**
|
|---|
| 9837 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 9838 | index f486651b6..92a74be74 100644
|
|---|
| 9839 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 9840 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 9841 | @@ -28,6 +28,7 @@ import org.openstreetmap.josm.data.osm.UniqueIdGenerator;
|
|---|
| 9842 | import org.openstreetmap.josm.gui.dialogs.relation.sort.RelationSorter;
|
|---|
| 9843 | import org.openstreetmap.josm.tools.Destroyable;
|
|---|
| 9844 | import org.openstreetmap.josm.tools.Geometry;
|
|---|
| 9845 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 9846 |
|
|---|
| 9847 | /**
|
|---|
| 9848 | * A data store for Vector Data sets
|
|---|
| 9849 | @@ -288,57 +289,83 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 9850 | * @param tile The tile to add
|
|---|
| 9851 | * @param <T> The tile type
|
|---|
| 9852 | */
|
|---|
| 9853 | - public synchronized <T extends Tile & VectorTile> void addTile(T tile) {
|
|---|
| 9854 | + public <T extends Tile & VectorTile> void addTile(T tile) {
|
|---|
| 9855 | Optional<Tile> previous = this.addedTiles.stream()
|
|---|
| 9856 | - .filter(t -> t.getTileXY().equals(tile.getTileXY()) && t.getZoom() == tile.getZoom()).findAny();
|
|---|
| 9857 | + .filter(t -> t.getTileXY().equals(tile.getTileXY()) && t.getZoom() == tile.getZoom()).findAny();
|
|---|
| 9858 | // Check if we have already added the tile (just to save processing time)
|
|---|
| 9859 | if (!previous.isPresent() || (!previous.get().isLoaded() && !previous.get().isLoading())) {
|
|---|
| 9860 | previous.ifPresent(this.addedTiles::remove);
|
|---|
| 9861 | this.addedTiles.add(tile);
|
|---|
| 9862 | - for (Layer layer : tile.getLayers()) {
|
|---|
| 9863 | - layer.getFeatures().forEach(feature -> {
|
|---|
| 9864 | - org.openstreetmap.josm.data.imagery.vectortile.mapbox.Geometry geometry = feature
|
|---|
| 9865 | - .getGeometryObject();
|
|---|
| 9866 | - List<VectorPrimitive> featureObjects = new ArrayList<>();
|
|---|
| 9867 | - List<VectorPrimitive> primaryFeatureObjects = new ArrayList<>();
|
|---|
| 9868 | - geometry.getShapes().forEach(shape -> {
|
|---|
| 9869 | - final VectorPrimitive primitive;
|
|---|
| 9870 | - if (shape instanceof Ellipse2D) {
|
|---|
| 9871 | - primitive = pointToNode(tile, layer, featureObjects,
|
|---|
| 9872 | - (int) ((Ellipse2D) shape).getCenterX(), (int) ((Ellipse2D) shape).getCenterY());
|
|---|
| 9873 | - } else if (shape instanceof Path2D) {
|
|---|
| 9874 | - primitive = pathToWay(tile, layer, featureObjects, (Path2D) shape).stream().findFirst()
|
|---|
| 9875 | - .orElse(null);
|
|---|
| 9876 | - } else if (shape instanceof Area) {
|
|---|
| 9877 | - primitive = areaToRelation(tile, layer, featureObjects, (Area) shape);
|
|---|
| 9878 | - primitive.put("type", "multipolygon");
|
|---|
| 9879 | - } else {
|
|---|
| 9880 | - // We shouldn't hit this, but just in case
|
|---|
| 9881 | - throw new UnsupportedOperationException();
|
|---|
| 9882 | - }
|
|---|
| 9883 | - primaryFeatureObjects.add(primitive);
|
|---|
| 9884 | - });
|
|---|
| 9885 | + VectorDataStore tStore = new VectorDataStore(this.dataSet, this.zoom);
|
|---|
| 9886 | + tStore.createDataTile(tile);
|
|---|
| 9887 | + try {
|
|---|
| 9888 | + this.getReadWriteLock().writeLock().lockInterruptibly();
|
|---|
| 9889 | + tStore.getAllPrimitives().forEach(this::addPrimitive);
|
|---|
| 9890 | + } catch (InterruptedException e) {
|
|---|
| 9891 | + Logging.error(e);
|
|---|
| 9892 | + Thread.currentThread().interrupt();
|
|---|
| 9893 | + } finally {
|
|---|
| 9894 | + if (this.getReadWriteLock().isWriteLockedByCurrentThread()) {
|
|---|
| 9895 | + this.getReadWriteLock().writeLock().unlock();
|
|---|
| 9896 | + }
|
|---|
| 9897 | + }
|
|---|
| 9898 | + }
|
|---|
| 9899 | + }
|
|---|
| 9900 | +
|
|---|
| 9901 | + private <T extends Tile & VectorTile> void createDataTile(T tile) {
|
|---|
| 9902 | + for (Layer layer : tile.getLayers()) {
|
|---|
| 9903 | + layer.getFeatures().forEach(feature -> {
|
|---|
| 9904 | + org.openstreetmap.josm.data.imagery.vectortile.mapbox.Geometry geometry = feature
|
|---|
| 9905 | + .getGeometryObject();
|
|---|
| 9906 | + List<VectorPrimitive> featureObjects = new ArrayList<>();
|
|---|
| 9907 | + List<VectorPrimitive> primaryFeatureObjects = new ArrayList<>();
|
|---|
| 9908 | + geometry.getShapes().forEach(shape -> {
|
|---|
| 9909 | final VectorPrimitive primitive;
|
|---|
| 9910 | - if (primaryFeatureObjects.size() == 1) {
|
|---|
| 9911 | - primitive = primaryFeatureObjects.get(0);
|
|---|
| 9912 | - if (primitive instanceof IRelation && !primitive.isMultipolygon()) {
|
|---|
| 9913 | - primitive.put(JOSM_MERGE_TYPE_KEY, "merge");
|
|---|
| 9914 | - }
|
|---|
| 9915 | - } else if (!primaryFeatureObjects.isEmpty()) {
|
|---|
| 9916 | - VectorRelation relation = new VectorRelation(layer.getName());
|
|---|
| 9917 | - primaryFeatureObjects.stream().map(prim -> new VectorRelationMember("", prim))
|
|---|
| 9918 | - .forEach(relation::addRelationMember);
|
|---|
| 9919 | - primitive = relation;
|
|---|
| 9920 | + if (shape instanceof Ellipse2D) {
|
|---|
| 9921 | + primitive = pointToNode(tile, layer, featureObjects,
|
|---|
| 9922 | + (int) ((Ellipse2D) shape).getCenterX(), (int) ((Ellipse2D) shape).getCenterY());
|
|---|
| 9923 | + } else if (shape instanceof Path2D) {
|
|---|
| 9924 | + primitive = pathToWay(tile, layer, featureObjects, (Path2D) shape).stream().findFirst()
|
|---|
| 9925 | + .orElse(null);
|
|---|
| 9926 | + } else if (shape instanceof Area) {
|
|---|
| 9927 | + primitive = areaToRelation(tile, layer, featureObjects, (Area) shape);
|
|---|
| 9928 | + primitive.put("type", "multipolygon");
|
|---|
| 9929 | } else {
|
|---|
| 9930 | - return;
|
|---|
| 9931 | + // We shouldn't hit this, but just in case
|
|---|
| 9932 | + throw new UnsupportedOperationException();
|
|---|
| 9933 | }
|
|---|
| 9934 | - primitive.setId(feature.getId());
|
|---|
| 9935 | - feature.getTags().forEach(primitive::put);
|
|---|
| 9936 | - featureObjects.forEach(this::addPrimitive);
|
|---|
| 9937 | - primaryFeatureObjects.forEach(this::addPrimitive);
|
|---|
| 9938 | - this.addPrimitive(primitive);
|
|---|
| 9939 | + primaryFeatureObjects.add(primitive);
|
|---|
| 9940 | });
|
|---|
| 9941 | - }
|
|---|
| 9942 | + final VectorPrimitive primitive;
|
|---|
| 9943 | + if (primaryFeatureObjects.size() == 1) {
|
|---|
| 9944 | + primitive = primaryFeatureObjects.get(0);
|
|---|
| 9945 | + if (primitive instanceof IRelation && !primitive.isMultipolygon()) {
|
|---|
| 9946 | + primitive.put(JOSM_MERGE_TYPE_KEY, "merge");
|
|---|
| 9947 | + }
|
|---|
| 9948 | + } else if (!primaryFeatureObjects.isEmpty()) {
|
|---|
| 9949 | + VectorRelation relation = new VectorRelation(layer.getName());
|
|---|
| 9950 | + primaryFeatureObjects.stream().map(prim -> new VectorRelationMember("", prim))
|
|---|
| 9951 | + .forEach(relation::addRelationMember);
|
|---|
| 9952 | + primitive = relation;
|
|---|
| 9953 | + } else {
|
|---|
| 9954 | + return;
|
|---|
| 9955 | + }
|
|---|
| 9956 | + primitive.setId(feature.getId());
|
|---|
| 9957 | + // Version 1 <i>does not guarantee</i> that non-zero ids are unique
|
|---|
| 9958 | + // We depend upon unique ids in the data store
|
|---|
| 9959 | + if (layer.getVersion() == 1 && feature.getId() != 0 && this.primitivesMap.containsKey(primitive.getPrimitiveId())) {
|
|---|
| 9960 | + // Reduce total memory usage by getting pre-existing strings, if they exist
|
|---|
| 9961 | + // Avoid interning, as the intern pool is known to be slow when many strings are added to it (Java 8)
|
|---|
| 9962 | + // HashSets tend to be a bit faster when interning many strings with relatively low usage
|
|---|
| 9963 | + String originalId = Long.toString(feature.getId());
|
|---|
| 9964 | + primitive.put("original_id", this.dataSet.allPrimitives().parallelStream().map(p -> p.get("original_id")).filter(originalId::equals).findAny().orElse(originalId));
|
|---|
| 9965 | + primitive.setId(primitive.getIdGenerator().generateUniqueId());
|
|---|
| 9966 | + }
|
|---|
| 9967 | + feature.getTags().forEach(primitive::put);
|
|---|
| 9968 | + featureObjects.forEach(this::addPrimitive);
|
|---|
| 9969 | + primaryFeatureObjects.forEach(this::addPrimitive);
|
|---|
| 9970 | + this.addPrimitive(primitive);
|
|---|
| 9971 | + });
|
|---|
| 9972 | }
|
|---|
| 9973 | }
|
|---|
| 9974 |
|
|---|
| 9975 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 9976 | index 38ab53ad2..a33c396f4 100644
|
|---|
| 9977 | --- a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 9978 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 9979 | @@ -15,6 +15,7 @@ import java.util.List;
|
|---|
| 9980 | import java.util.Map;
|
|---|
| 9981 | import java.util.stream.Collectors;
|
|---|
| 9982 |
|
|---|
| 9983 | +import org.junit.jupiter.api.RepeatedTest;
|
|---|
| 9984 | import org.openstreetmap.josm.TestUtils;
|
|---|
| 9985 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 9986 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 9987 | @@ -113,13 +114,17 @@ class VectorDataSetTest {
|
|---|
| 9988 | dataSet.setZoom(14);
|
|---|
| 9989 | loadTile(this.layer, 14, 3248, 6258);
|
|---|
| 9990 |
|
|---|
| 9991 | - // There _does_ appear to be some kind of race condition though
|
|---|
| 9992 | - Awaitility.await().atMost(Durations.FIVE_SECONDS).until(() -> dataSet.getNodes().size() > 50);
|
|---|
| 9993 | // Actual test
|
|---|
| 9994 | // With Mapillary, only ends of ways should be untagged
|
|---|
| 9995 | // There are 55 actual "nodes" in the data with two nodes for the ends of the way.
|
|---|
| 9996 | // One of the end nodes is a duplicate of an actual node.
|
|---|
| 9997 | assertEquals(56, dataSet.getNodes().size());
|
|---|
| 9998 | + // There should be 55 nodes from the mapillary-images layer
|
|---|
| 9999 | + assertEquals(55, dataSet.getNodes().stream().filter(node -> "mapillary-images".equals(node.getLayer())).count());
|
|---|
| 10000 | + // Please note that this dataset originally had the <i>same</i> id for all the images
|
|---|
| 10001 | + // (MVT v2 explicitly said that ids had to be unique in a layer, MVT v1 did not)
|
|---|
| 10002 | + assertEquals(55, dataSet.getNodes().stream().map(node -> node.get("original_id")).count());
|
|---|
| 10003 | + assertEquals(1, dataSet.getNodes().stream().map(node -> node.get("original_id")).distinct().count());
|
|---|
| 10004 | assertEquals(1, dataSet.getWays().size());
|
|---|
| 10005 | assertEquals(0, dataSet.getRelations().size());
|
|---|
| 10006 | }
|
|---|
| 10007 | --
|
|---|
| 10008 | GitLab
|
|---|
| 10009 |
|
|---|
| 10010 |
|
|---|
| 10011 | From 8ea1c070ed9de9cf0f02d209713a862f0f5f960d Mon Sep 17 00:00:00 2001
|
|---|
| 10012 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10013 | Date: Wed, 21 Apr 2021 16:44:57 -0600
|
|---|
| 10014 | Subject: [PATCH 12/50] Add layer filtering back in
|
|---|
| 10015 |
|
|---|
| 10016 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10017 | ---
|
|---|
| 10018 | .../josm/data/vector/VectorDataSet.java | 21 +++++++++++++-
|
|---|
| 10019 | .../josm/data/vector/VectorDataStore.java | 28 ++++++++++++++++---
|
|---|
| 10020 | .../josm/data/vector/VectorPrimitive.java | 5 ++++
|
|---|
| 10021 | .../josm/gui/layer/imagery/MVTLayer.java | 1 +
|
|---|
| 10022 | 4 files changed, 50 insertions(+), 5 deletions(-)
|
|---|
| 10023 |
|
|---|
| 10024 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 10025 | index 8e18dae25..4e9cb9ffd 100644
|
|---|
| 10026 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 10027 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 10028 | @@ -49,6 +49,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 10029 | // Both of these listener lists are useless, since they expect OsmPrimitives at this time
|
|---|
| 10030 | private final ListenerList<HighlightUpdateListener> highlightUpdateListenerListenerList = ListenerList.create();
|
|---|
| 10031 | private final ListenerList<DataSelectionListener> dataSelectionListenerListenerList = ListenerList.create();
|
|---|
| 10032 | + private static final String[] NO_INVISIBLE_LAYERS = new String[0];
|
|---|
| 10033 | + private String[] invisibleLayers = NO_INVISIBLE_LAYERS;
|
|---|
| 10034 | private boolean lock = true;
|
|---|
| 10035 | private String name;
|
|---|
| 10036 | private short mappaintCacheIdx = 1;
|
|---|
| 10037 | @@ -474,7 +476,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 10038 | final int currentZoom = tile.getZoom();
|
|---|
| 10039 | // computeIfAbsent should be thread safe (ConcurrentHashMap indicates it is, anyway)
|
|---|
| 10040 | final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 10041 | - dataStore.addTile(tile);
|
|---|
| 10042 | + dataStore.addTile(tile, this.invisibleLayers);
|
|---|
| 10043 | }
|
|---|
| 10044 |
|
|---|
| 10045 | /**
|
|---|
| 10046 | @@ -538,4 +540,21 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 10047 | this.styles = null;
|
|---|
| 10048 | }
|
|---|
| 10049 | }
|
|---|
| 10050 | +
|
|---|
| 10051 | + /**
|
|---|
| 10052 | + * Mark some layers as invisible
|
|---|
| 10053 | + * @param invisibleLayers The layer to not show
|
|---|
| 10054 | + */
|
|---|
| 10055 | + public void setInvisibleLayers(Collection<String> invisibleLayers) {
|
|---|
| 10056 | + if (invisibleLayers == null || invisibleLayers.isEmpty() || invisibleLayers.stream().filter(Objects::nonNull).filter(string -> !string.isEmpty()).count() == 0) {
|
|---|
| 10057 | + this.invisibleLayers = NO_INVISIBLE_LAYERS;
|
|---|
| 10058 | + return;
|
|---|
| 10059 | + }
|
|---|
| 10060 | + String[] currentInvisibleLayers = invisibleLayers.stream().filter(Objects::nonNull).toArray(String[]::new);
|
|---|
| 10061 | + this.invisibleLayers = currentInvisibleLayers;
|
|---|
| 10062 | + List<String> temporaryList = Arrays.asList(currentInvisibleLayers);
|
|---|
| 10063 | + this.dataStoreMap.values().forEach(dataStore -> {
|
|---|
| 10064 | + dataStore.getAllPrimitives().parallelStream().forEach(primitive -> primitive.setVisible(!temporaryList.contains(primitive.getLayer())));
|
|---|
| 10065 | + });
|
|---|
| 10066 | + }
|
|---|
| 10067 | }
|
|---|
| 10068 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10069 | index 92a74be74..4f2e37252 100644
|
|---|
| 10070 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10071 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10072 | @@ -6,6 +6,7 @@ import java.awt.geom.Ellipse2D;
|
|---|
| 10073 | import java.awt.geom.Path2D;
|
|---|
| 10074 | import java.awt.geom.PathIterator;
|
|---|
| 10075 | import java.util.ArrayList;
|
|---|
| 10076 | +import java.util.Arrays;
|
|---|
| 10077 | import java.util.Collection;
|
|---|
| 10078 | import java.util.Collections;
|
|---|
| 10079 | import java.util.List;
|
|---|
| 10080 | @@ -28,14 +29,17 @@ import org.openstreetmap.josm.data.osm.UniqueIdGenerator;
|
|---|
| 10081 | import org.openstreetmap.josm.gui.dialogs.relation.sort.RelationSorter;
|
|---|
| 10082 | import org.openstreetmap.josm.tools.Destroyable;
|
|---|
| 10083 | import org.openstreetmap.josm.tools.Geometry;
|
|---|
| 10084 | +import org.openstreetmap.josm.tools.JosmRuntimeException;
|
|---|
| 10085 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 10086 |
|
|---|
| 10087 | +
|
|---|
| 10088 | /**
|
|---|
| 10089 | * A data store for Vector Data sets
|
|---|
| 10090 | * @author Taylor Smock
|
|---|
| 10091 | * @since xxx
|
|---|
| 10092 | */
|
|---|
| 10093 | class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay, VectorRelation> implements Destroyable {
|
|---|
| 10094 | + private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|---|
| 10095 | private static final String JOSM_MERGE_TYPE_KEY = "josm_merge_type";
|
|---|
| 10096 | private final VectorDataSet dataSet;
|
|---|
| 10097 |
|
|---|
| 10098 | @@ -174,7 +178,7 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10099 | private synchronized <T extends Tile & VectorTile> VectorNode pointToNode(T tile, Layer layer,
|
|---|
| 10100 | Collection<VectorPrimitive> featureObjects, int x, int y) {
|
|---|
| 10101 | final ICoordinate upperLeft = tile.getTileSource().tileXYToLatLon(tile);
|
|---|
| 10102 | - final int layerExtent = layer.getExtent() * 2;
|
|---|
| 10103 | + final int layerExtent = layer.getExtent();
|
|---|
| 10104 | final ICoordinate lowerRight = tile.getTileSource()
|
|---|
| 10105 | .tileXYToLatLon(tile.getXtile() + 1, tile.getYtile() + 1, tile.getZoom());
|
|---|
| 10106 | final ICoordinate coords = new Coordinate(
|
|---|
| 10107 | @@ -286,10 +290,21 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10108 |
|
|---|
| 10109 | /**
|
|---|
| 10110 | * Add a tile to this data store
|
|---|
| 10111 | + * @param <T> The tile type
|
|---|
| 10112 | * @param tile The tile to add
|
|---|
| 10113 | + */
|
|---|
| 10114 | + public synchronized <T extends Tile & VectorTile> void addTile(T tile) {
|
|---|
| 10115 | + addTile(tile, EMPTY_STRING_ARRAY);
|
|---|
| 10116 | + }
|
|---|
| 10117 | +
|
|---|
| 10118 | + /**
|
|---|
| 10119 | + * Add a tile to this data store
|
|---|
| 10120 | * @param <T> The tile type
|
|---|
| 10121 | + * @param tile The tile to add
|
|---|
| 10122 | + * @param invisibleLayers Any invisible current invisible layers
|
|---|
| 10123 | */
|
|---|
| 10124 | - public <T extends Tile & VectorTile> void addTile(T tile) {
|
|---|
| 10125 | + public <T extends Tile & VectorTile> void addTile(T tile, String[] invisibleLayers) {
|
|---|
| 10126 | + List<String> invisibleLayerList = Arrays.asList(invisibleLayers);
|
|---|
| 10127 | Optional<Tile> previous = this.addedTiles.stream()
|
|---|
| 10128 | .filter(t -> t.getTileXY().equals(tile.getTileXY()) && t.getZoom() == tile.getZoom()).findAny();
|
|---|
| 10129 | // Check if we have already added the tile (just to save processing time)
|
|---|
| 10130 | @@ -297,7 +312,7 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10131 | previous.ifPresent(this.addedTiles::remove);
|
|---|
| 10132 | this.addedTiles.add(tile);
|
|---|
| 10133 | VectorDataStore tStore = new VectorDataStore(this.dataSet, this.zoom);
|
|---|
| 10134 | - tStore.createDataTile(tile);
|
|---|
| 10135 | + tStore.createDataTile(tile, invisibleLayerList);
|
|---|
| 10136 | try {
|
|---|
| 10137 | this.getReadWriteLock().writeLock().lockInterruptibly();
|
|---|
| 10138 | tStore.getAllPrimitives().forEach(this::addPrimitive);
|
|---|
| 10139 | @@ -312,7 +327,7 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10140 | }
|
|---|
| 10141 | }
|
|---|
| 10142 |
|
|---|
| 10143 | - private <T extends Tile & VectorTile> void createDataTile(T tile) {
|
|---|
| 10144 | + private <T extends Tile & VectorTile> void createDataTile(T tile, List<String> invisibleLayerList) {
|
|---|
| 10145 | for (Layer layer : tile.getLayers()) {
|
|---|
| 10146 | layer.getFeatures().forEach(feature -> {
|
|---|
| 10147 | org.openstreetmap.josm.data.imagery.vectortile.mapbox.Geometry geometry = feature
|
|---|
| 10148 | @@ -364,6 +379,11 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10149 | feature.getTags().forEach(primitive::put);
|
|---|
| 10150 | featureObjects.forEach(this::addPrimitive);
|
|---|
| 10151 | primaryFeatureObjects.forEach(this::addPrimitive);
|
|---|
| 10152 | + if (invisibleLayerList.contains(primitive.getLayer())) {
|
|---|
| 10153 | + primitive.setVisible(false);
|
|---|
| 10154 | + featureObjects.forEach(p -> p.setVisible(false));
|
|---|
| 10155 | + primaryFeatureObjects.forEach(p -> p.setVisible(false));
|
|---|
| 10156 | + }
|
|---|
| 10157 | this.addPrimitive(primitive);
|
|---|
| 10158 | });
|
|---|
| 10159 | }
|
|---|
| 10160 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java b/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 10161 | index 17b5bef6f..ed9c93937 100644
|
|---|
| 10162 | --- a/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 10163 | +++ b/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 10164 | @@ -253,4 +253,9 @@ public abstract class VectorPrimitive extends AbstractPrimitive implements DataL
|
|---|
| 10165 | public String getLayer() {
|
|---|
| 10166 | return this.layer;
|
|---|
| 10167 | }
|
|---|
| 10168 | +
|
|---|
| 10169 | + @Override
|
|---|
| 10170 | + public boolean isDrawable() {
|
|---|
| 10171 | + return super.isDrawable() && this.isVisible();
|
|---|
| 10172 | + }
|
|---|
| 10173 | }
|
|---|
| 10174 | diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10175 | index aa335f7b0..4007e8495 100644
|
|---|
| 10176 | --- a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10177 | +++ b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10178 | @@ -138,6 +138,7 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 10179 | actions.add(new EnableLayerAction(layerConfig.getKey(), () -> layerNames.computeIfAbsent(layerConfig.getKey(), key -> true),
|
|---|
| 10180 | layer -> {
|
|---|
| 10181 | layerNames.compute(layer, (key, value) -> Boolean.FALSE.equals(value));
|
|---|
| 10182 | + this.dataSet.setInvisibleLayers(layerNames.entrySet().stream().filter(entry -> Boolean.FALSE.equals(entry.getValue())).map(Map.Entry::getKey).collect(Collectors.toList()));
|
|---|
| 10183 | this.invalidate();
|
|---|
| 10184 | }));
|
|---|
| 10185 | }
|
|---|
| 10186 | --
|
|---|
| 10187 | GitLab
|
|---|
| 10188 |
|
|---|
| 10189 |
|
|---|
| 10190 | From 5f86499538c07e0c2b7659dcb5b2071e49021624 Mon Sep 17 00:00:00 2001
|
|---|
| 10191 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10192 | Date: Wed, 21 Apr 2021 16:45:33 -0600
|
|---|
| 10193 | Subject: [PATCH 13/50] MvtLayer: Add additional information to objects when
|
|---|
| 10194 | converting
|
|---|
| 10195 |
|
|---|
| 10196 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10197 | ---
|
|---|
| 10198 | .../josm/gui/layer/imagery/MVTLayer.java | 14 ++++++++++----
|
|---|
| 10199 | 1 file changed, 10 insertions(+), 4 deletions(-)
|
|---|
| 10200 |
|
|---|
| 10201 | diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10202 | index 4007e8495..d0b98d633 100644
|
|---|
| 10203 | --- a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10204 | +++ b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10205 | @@ -173,6 +173,8 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 10206 | Node newNode = new Node(vectorNode.getCoor());
|
|---|
| 10207 | if (vectorNode.isTagged()) {
|
|---|
| 10208 | vectorNode.getInterestingTags().forEach(newNode::put);
|
|---|
| 10209 | + newNode.put("layer", vectorNode.getLayer());
|
|---|
| 10210 | + newNode.put("id", Long.toString(vectorNode.getId()));
|
|---|
| 10211 | }
|
|---|
| 10212 | nodeMap.put(vectorNode, newNode);
|
|---|
| 10213 | }
|
|---|
| 10214 | @@ -184,6 +186,8 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 10215 | newWay.setNodes(nodes);
|
|---|
| 10216 | if (vectorWay.isTagged()) {
|
|---|
| 10217 | vectorWay.getInterestingTags().forEach(newWay::put);
|
|---|
| 10218 | + newWay.put("layer", vectorWay.getLayer());
|
|---|
| 10219 | + newWay.put("id", Long.toString(vectorWay.getId()));
|
|---|
| 10220 | }
|
|---|
| 10221 | wayMap.put(vectorWay, newWay);
|
|---|
| 10222 | }
|
|---|
| 10223 | @@ -191,9 +195,11 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 10224 | // Finally, add Relations
|
|---|
| 10225 | Map<VectorRelation, Relation> relationMap = new HashMap<>(dataSet.getRelations().size());
|
|---|
| 10226 | for (VectorRelation vectorRelation : dataSet.getRelations()) {
|
|---|
| 10227 | - Relation relation = new Relation();
|
|---|
| 10228 | + Relation newRelation = new Relation();
|
|---|
| 10229 | if (vectorRelation.isTagged()) {
|
|---|
| 10230 | - vectorRelation.getInterestingTags().forEach(relation::put);
|
|---|
| 10231 | + vectorRelation.getInterestingTags().forEach(newRelation::put);
|
|---|
| 10232 | + newRelation.put("layer", vectorRelation.getLayer());
|
|---|
| 10233 | + newRelation.put("id", Long.toString(vectorRelation.getId()));
|
|---|
| 10234 | }
|
|---|
| 10235 | List<RelationMember> members = vectorRelation.getMembers().stream().map(member -> {
|
|---|
| 10236 | final OsmPrimitive primitive;
|
|---|
| 10237 | @@ -211,8 +217,8 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 10238 | if (primitive == null) return null;
|
|---|
| 10239 | return new RelationMember(member.getRole(), primitive);
|
|---|
| 10240 | }).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 10241 | - relation.setMembers(members);
|
|---|
| 10242 | - relationMap.put(vectorRelation, relation);
|
|---|
| 10243 | + newRelation.setMembers(members);
|
|---|
| 10244 | + relationMap.put(vectorRelation, newRelation);
|
|---|
| 10245 | }
|
|---|
| 10246 | try {
|
|---|
| 10247 | osmData.beginUpdate();
|
|---|
| 10248 | --
|
|---|
| 10249 | GitLab
|
|---|
| 10250 |
|
|---|
| 10251 |
|
|---|
| 10252 | From abb734cd52710755b287f596521510f7659f4dbb Mon Sep 17 00:00:00 2001
|
|---|
| 10253 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10254 | Date: Wed, 21 Apr 2021 17:01:57 -0600
|
|---|
| 10255 | Subject: [PATCH 14/50] VectorDataStore: Don't attempt to deduplicate ways
|
|---|
| 10256 |
|
|---|
| 10257 | This has led to some ConcurrentModificationExceptions and/or primitives
|
|---|
| 10258 | that cannot be removed. This functionality should be done in a different
|
|---|
| 10259 | ticket.
|
|---|
| 10260 |
|
|---|
| 10261 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10262 | ---
|
|---|
| 10263 | .../josm/data/vector/DataStore.java | 2 +-
|
|---|
| 10264 | .../josm/data/vector/VectorDataStore.java | 35 +++++--------------
|
|---|
| 10265 | 2 files changed, 10 insertions(+), 27 deletions(-)
|
|---|
| 10266 |
|
|---|
| 10267 | diff --git a/src/org/openstreetmap/josm/data/vector/DataStore.java b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 10268 | index 5175a534b..86f1948d1 100644
|
|---|
| 10269 | --- a/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 10270 | +++ b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 10271 | @@ -48,7 +48,7 @@ class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R exte
|
|---|
| 10272 | protected final LocalQuadBucketPrimitiveStore<N, W, R> store = new LocalQuadBucketPrimitiveStore<>();
|
|---|
| 10273 | protected final Storage<O> allPrimitives = new Storage<>(new Storage.PrimitiveIdHash(), true);
|
|---|
| 10274 | // TODO what happens when I use hashCode?
|
|---|
| 10275 | - protected final Set<Tile> addedTiles = new HashSet<>();
|
|---|
| 10276 | + protected final Set<Tile> addedTiles = Collections.synchronizedSet(new HashSet<>());
|
|---|
| 10277 | protected final Map<PrimitiveId, O> primitivesMap = allPrimitives
|
|---|
| 10278 | .foreignKey(new Storage.PrimitiveIdHash());
|
|---|
| 10279 | protected final Collection<DataSource> dataSources = new LinkedList<>();
|
|---|
| 10280 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10281 | index 4f2e37252..cd5a03fe9 100644
|
|---|
| 10282 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10283 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10284 | @@ -9,8 +9,8 @@ import java.util.ArrayList;
|
|---|
| 10285 | import java.util.Arrays;
|
|---|
| 10286 | import java.util.Collection;
|
|---|
| 10287 | import java.util.Collections;
|
|---|
| 10288 | +import java.util.HashSet;
|
|---|
| 10289 | import java.util.List;
|
|---|
| 10290 | -import java.util.Objects;
|
|---|
| 10291 | import java.util.Optional;
|
|---|
| 10292 | import java.util.stream.Collectors;
|
|---|
| 10293 |
|
|---|
| 10294 | @@ -127,24 +127,7 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10295 | iteration++;
|
|---|
| 10296 | relationWayList.removeIf(wayList::contains);
|
|---|
| 10297 | }
|
|---|
| 10298 | - if (!relationWayList.isEmpty()) {
|
|---|
| 10299 | - return relation;
|
|---|
| 10300 | - }
|
|---|
| 10301 | - // Merge ways
|
|---|
| 10302 | - List<VectorNode> nodes = new ArrayList<>();
|
|---|
| 10303 | - for (VectorWay way : wayList) {
|
|---|
| 10304 | - for (VectorNode node : way.getNodes()) {
|
|---|
| 10305 | - if (nodes.isEmpty() || !Objects.equals(nodes.get(nodes.size() - 1), node)) {
|
|---|
| 10306 | - nodes.add(node);
|
|---|
| 10307 | - }
|
|---|
| 10308 | - }
|
|---|
| 10309 | - }
|
|---|
| 10310 | - VectorWay way = wayList.get(0);
|
|---|
| 10311 | - way.setNodes(nodes);
|
|---|
| 10312 | - wayList.remove(way);
|
|---|
| 10313 | - wayList.forEach(this::removePrimitive);
|
|---|
| 10314 | - this.removePrimitive(relation);
|
|---|
| 10315 | - return way;
|
|---|
| 10316 | + return relation;
|
|---|
| 10317 | }
|
|---|
| 10318 |
|
|---|
| 10319 | private static <N extends INode, W extends IWay<N>> boolean canMergeWays(W old, W toAdd, boolean allowReverse) {
|
|---|
| 10320 | @@ -305,8 +288,11 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10321 | */
|
|---|
| 10322 | public <T extends Tile & VectorTile> void addTile(T tile, String[] invisibleLayers) {
|
|---|
| 10323 | List<String> invisibleLayerList = Arrays.asList(invisibleLayers);
|
|---|
| 10324 | - Optional<Tile> previous = this.addedTiles.stream()
|
|---|
| 10325 | - .filter(t -> t.getTileXY().equals(tile.getTileXY()) && t.getZoom() == tile.getZoom()).findAny();
|
|---|
| 10326 | + Optional<Tile> previous;
|
|---|
| 10327 | + synchronized (this.addedTiles) {
|
|---|
| 10328 | + previous = this.addedTiles.stream()
|
|---|
| 10329 | + .filter(t -> t.getTileXY().equals(tile.getTileXY()) && t.getZoom() == tile.getZoom()).findAny();
|
|---|
| 10330 | + }
|
|---|
| 10331 | // Check if we have already added the tile (just to save processing time)
|
|---|
| 10332 | if (!previous.isPresent() || (!previous.get().isLoaded() && !previous.get().isLoading())) {
|
|---|
| 10333 | previous.ifPresent(this.addedTiles::remove);
|
|---|
| 10334 | @@ -369,11 +355,8 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10335 | // Version 1 <i>does not guarantee</i> that non-zero ids are unique
|
|---|
| 10336 | // We depend upon unique ids in the data store
|
|---|
| 10337 | if (layer.getVersion() == 1 && feature.getId() != 0 && this.primitivesMap.containsKey(primitive.getPrimitiveId())) {
|
|---|
| 10338 | - // Reduce total memory usage by getting pre-existing strings, if they exist
|
|---|
| 10339 | - // Avoid interning, as the intern pool is known to be slow when many strings are added to it (Java 8)
|
|---|
| 10340 | - // HashSets tend to be a bit faster when interning many strings with relatively low usage
|
|---|
| 10341 | - String originalId = Long.toString(feature.getId());
|
|---|
| 10342 | - primitive.put("original_id", this.dataSet.allPrimitives().parallelStream().map(p -> p.get("original_id")).filter(originalId::equals).findAny().orElse(originalId));
|
|---|
| 10343 | + // This, unfortunately, makes a new string
|
|---|
| 10344 | + primitive.put("original_id", Long.toString(feature.getId()));
|
|---|
| 10345 | primitive.setId(primitive.getIdGenerator().generateUniqueId());
|
|---|
| 10346 | }
|
|---|
| 10347 | feature.getTags().forEach(primitive::put);
|
|---|
| 10348 | --
|
|---|
| 10349 | GitLab
|
|---|
| 10350 |
|
|---|
| 10351 |
|
|---|
| 10352 | From 676ec09fa4726006134816f152c00e65364b49dd Mon Sep 17 00:00:00 2001
|
|---|
| 10353 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10354 | Date: Thu, 22 Apr 2021 08:03:40 -0600
|
|---|
| 10355 | Subject: [PATCH 15/50] MVTLayer: Make some options expert only
|
|---|
| 10356 |
|
|---|
| 10357 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10358 | ---
|
|---|
| 10359 | .../josm/gui/layer/imagery/MVTLayer.java | 23 +++++++++++--------
|
|---|
| 10360 | 1 file changed, 13 insertions(+), 10 deletions(-)
|
|---|
| 10361 |
|
|---|
| 10362 | diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10363 | index d0b98d633..876ce5399 100644
|
|---|
| 10364 | --- a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10365 | +++ b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10366 | @@ -25,6 +25,7 @@ import javax.swing.JMenuItem;
|
|---|
| 10367 |
|
|---|
| 10368 | import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 10369 | import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
|
|---|
| 10370 | +import org.openstreetmap.josm.actions.ExpertToggleAction;
|
|---|
| 10371 | import org.openstreetmap.josm.data.Bounds;
|
|---|
| 10372 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 10373 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 10374 | @@ -134,17 +135,19 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 10375 | ArrayList<Action> actions = new ArrayList<>(Arrays.asList(super.getMenuEntries()));
|
|---|
| 10376 | // Add separator between Info and the layers
|
|---|
| 10377 | actions.add(SeparatorLayerAction.INSTANCE);
|
|---|
| 10378 | - for (Map.Entry<String, Boolean> layerConfig : layerNames.entrySet()) {
|
|---|
| 10379 | - actions.add(new EnableLayerAction(layerConfig.getKey(), () -> layerNames.computeIfAbsent(layerConfig.getKey(), key -> true),
|
|---|
| 10380 | - layer -> {
|
|---|
| 10381 | - layerNames.compute(layer, (key, value) -> Boolean.FALSE.equals(value));
|
|---|
| 10382 | - this.dataSet.setInvisibleLayers(layerNames.entrySet().stream().filter(entry -> Boolean.FALSE.equals(entry.getValue())).map(Map.Entry::getKey).collect(Collectors.toList()));
|
|---|
| 10383 | - this.invalidate();
|
|---|
| 10384 | - }));
|
|---|
| 10385 | + if (ExpertToggleAction.isExpert()) {
|
|---|
| 10386 | + for (Map.Entry<String, Boolean> layerConfig : layerNames.entrySet()) {
|
|---|
| 10387 | + actions.add(new EnableLayerAction(layerConfig.getKey(), () -> layerNames.computeIfAbsent(layerConfig.getKey(), key -> true),
|
|---|
| 10388 | + layer -> {
|
|---|
| 10389 | + layerNames.compute(layer, (key, value) -> Boolean.FALSE.equals(value));
|
|---|
| 10390 | + this.dataSet.setInvisibleLayers(layerNames.entrySet().stream().filter(entry -> Boolean.FALSE.equals(entry.getValue())).map(Map.Entry::getKey).collect(Collectors.toList()));
|
|---|
| 10391 | + this.invalidate();
|
|---|
| 10392 | + }));
|
|---|
| 10393 | + }
|
|---|
| 10394 | + // Add separator between layers and convert action
|
|---|
| 10395 | + actions.add(SeparatorLayerAction.INSTANCE);
|
|---|
| 10396 | + actions.add(new ConvertLayerAction(this));
|
|---|
| 10397 | }
|
|---|
| 10398 | - // Add separator between layers and convert action
|
|---|
| 10399 | - actions.add(SeparatorLayerAction.INSTANCE);
|
|---|
| 10400 | - actions.add(new ConvertLayerAction(this));
|
|---|
| 10401 | return actions.toArray(EMPTY_ACTIONS);
|
|---|
| 10402 | }
|
|---|
| 10403 |
|
|---|
| 10404 | --
|
|---|
| 10405 | GitLab
|
|---|
| 10406 |
|
|---|
| 10407 |
|
|---|
| 10408 | From 88b205a99a757dd4d929eca11d66b474decf698d Mon Sep 17 00:00:00 2001
|
|---|
| 10409 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10410 | Date: Thu, 22 Apr 2021 08:04:00 -0600
|
|---|
| 10411 | Subject: [PATCH 16/50] MVT v1 and v2 don't *require* that ids be unique.
|
|---|
| 10412 |
|
|---|
| 10413 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10414 | ---
|
|---|
| 10415 | .../josm/data/vector/VectorDataStore.java | 26 ++++++++++++++++---
|
|---|
| 10416 | 1 file changed, 22 insertions(+), 4 deletions(-)
|
|---|
| 10417 |
|
|---|
| 10418 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10419 | index cd5a03fe9..34c5dbd47 100644
|
|---|
| 10420 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10421 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10422 | @@ -9,11 +9,17 @@ import java.util.ArrayList;
|
|---|
| 10423 | import java.util.Arrays;
|
|---|
| 10424 | import java.util.Collection;
|
|---|
| 10425 | import java.util.Collections;
|
|---|
| 10426 | +import java.util.HashMap;
|
|---|
| 10427 | import java.util.HashSet;
|
|---|
| 10428 | import java.util.List;
|
|---|
| 10429 | +import java.util.Map;
|
|---|
| 10430 | +import java.util.Objects;
|
|---|
| 10431 | import java.util.Optional;
|
|---|
| 10432 | import java.util.stream.Collectors;
|
|---|
| 10433 | +import java.util.stream.Stream;
|
|---|
| 10434 |
|
|---|
| 10435 | +import com.google.common.base.Functions;
|
|---|
| 10436 | +import org.antlr.v4.runtime.atn.SemanticContext;
|
|---|
| 10437 | import org.openstreetmap.gui.jmapviewer.Coordinate;
|
|---|
| 10438 | import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 10439 | import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
|---|
| 10440 | @@ -21,6 +27,7 @@ import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 10441 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 10442 | import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 10443 | import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 10444 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 10445 | import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 10446 | import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 10447 | import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 10448 | @@ -41,6 +48,7 @@ import org.openstreetmap.josm.tools.Logging;
|
|---|
| 10449 | class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay, VectorRelation> implements Destroyable {
|
|---|
| 10450 | private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|---|
| 10451 | private static final String JOSM_MERGE_TYPE_KEY = "josm_merge_type";
|
|---|
| 10452 | + private static final String ORIGINAL_ID = "original_id";
|
|---|
| 10453 | private final VectorDataSet dataSet;
|
|---|
| 10454 |
|
|---|
| 10455 | VectorDataStore(VectorDataSet dataSet, int zoom) {
|
|---|
| 10456 | @@ -352,11 +360,11 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10457 | return;
|
|---|
| 10458 | }
|
|---|
| 10459 | primitive.setId(feature.getId());
|
|---|
| 10460 | - // Version 1 <i>does not guarantee</i> that non-zero ids are unique
|
|---|
| 10461 | + // Version 1 <i>and</i> 2 <i>do not guarantee</i> that non-zero ids are unique
|
|---|
| 10462 | // We depend upon unique ids in the data store
|
|---|
| 10463 | - if (layer.getVersion() == 1 && feature.getId() != 0 && this.primitivesMap.containsKey(primitive.getPrimitiveId())) {
|
|---|
| 10464 | + if (feature.getId() != 0 && this.primitivesMap.containsKey(primitive.getPrimitiveId())) {
|
|---|
| 10465 | // This, unfortunately, makes a new string
|
|---|
| 10466 | - primitive.put("original_id", Long.toString(feature.getId()));
|
|---|
| 10467 | + primitive.put(ORIGINAL_ID, Long.toString(feature.getId()));
|
|---|
| 10468 | primitive.setId(primitive.getIdGenerator().generateUniqueId());
|
|---|
| 10469 | }
|
|---|
| 10470 | feature.getTags().forEach(primitive::put);
|
|---|
| 10471 | @@ -367,9 +375,19 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10472 | featureObjects.forEach(p -> p.setVisible(false));
|
|---|
| 10473 | primaryFeatureObjects.forEach(p -> p.setVisible(false));
|
|---|
| 10474 | }
|
|---|
| 10475 | - this.addPrimitive(primitive);
|
|---|
| 10476 | + try {
|
|---|
| 10477 | + this.addPrimitive(primitive);
|
|---|
| 10478 | + } catch (JosmRuntimeException e) {
|
|---|
| 10479 | + Logging.error("{0}/{1}/{2}: {3}", tile.getZoom(), tile.getXtile(), tile.getYtile(), primitive.get("key"));
|
|---|
| 10480 | + throw e;
|
|---|
| 10481 | + }
|
|---|
| 10482 | });
|
|---|
| 10483 | }
|
|---|
| 10484 | + // Replace original_ids with the same object (reduce memory usage)
|
|---|
| 10485 | + // Strings aren't interned automatically (see
|
|---|
| 10486 | + Collection<IPrimitive> primitives = this.dataSet.allPrimitives().stream().filter(p -> p.hasKey(ORIGINAL_ID)).collect(Collectors.toList());
|
|---|
| 10487 | + List<String> toReplace = primitives.stream().map(p -> p.get(ORIGINAL_ID)).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 10488 | + primitives.stream().filter(p -> toReplace.contains(p.get(ORIGINAL_ID))).forEach(p -> p.put(ORIGINAL_ID, toReplace.stream().filter(shared -> shared.equals(p.get(ORIGINAL_ID))).findAny().orElse(null)));
|
|---|
| 10489 | }
|
|---|
| 10490 |
|
|---|
| 10491 | @Override
|
|---|
| 10492 | --
|
|---|
| 10493 | GitLab
|
|---|
| 10494 |
|
|---|
| 10495 |
|
|---|
| 10496 | From 48a97b203f0f6a091fdf698fd4ba75b0a9449feb Mon Sep 17 00:00:00 2001
|
|---|
| 10497 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10498 | Date: Thu, 22 Apr 2021 08:21:10 -0600
|
|---|
| 10499 | Subject: [PATCH 17/50] FIXUP: VectorDataStore: Optimize imports
|
|---|
| 10500 |
|
|---|
| 10501 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10502 | ---
|
|---|
| 10503 | .../josm/data/vector/VectorDataStore.java | 32 ++++++++-----------
|
|---|
| 10504 | 1 file changed, 13 insertions(+), 19 deletions(-)
|
|---|
| 10505 |
|
|---|
| 10506 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10507 | index 34c5dbd47..bd76e3538 100644
|
|---|
| 10508 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10509 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10510 | @@ -1,25 +1,6 @@
|
|---|
| 10511 | // License: GPL. For details, see LICENSE file.
|
|---|
| 10512 | package org.openstreetmap.josm.data.vector;
|
|---|
| 10513 |
|
|---|
| 10514 | -import java.awt.geom.Area;
|
|---|
| 10515 | -import java.awt.geom.Ellipse2D;
|
|---|
| 10516 | -import java.awt.geom.Path2D;
|
|---|
| 10517 | -import java.awt.geom.PathIterator;
|
|---|
| 10518 | -import java.util.ArrayList;
|
|---|
| 10519 | -import java.util.Arrays;
|
|---|
| 10520 | -import java.util.Collection;
|
|---|
| 10521 | -import java.util.Collections;
|
|---|
| 10522 | -import java.util.HashMap;
|
|---|
| 10523 | -import java.util.HashSet;
|
|---|
| 10524 | -import java.util.List;
|
|---|
| 10525 | -import java.util.Map;
|
|---|
| 10526 | -import java.util.Objects;
|
|---|
| 10527 | -import java.util.Optional;
|
|---|
| 10528 | -import java.util.stream.Collectors;
|
|---|
| 10529 | -import java.util.stream.Stream;
|
|---|
| 10530 | -
|
|---|
| 10531 | -import com.google.common.base.Functions;
|
|---|
| 10532 | -import org.antlr.v4.runtime.atn.SemanticContext;
|
|---|
| 10533 | import org.openstreetmap.gui.jmapviewer.Coordinate;
|
|---|
| 10534 | import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 10535 | import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
|---|
| 10536 | @@ -39,6 +20,19 @@ import org.openstreetmap.josm.tools.Geometry;
|
|---|
| 10537 | import org.openstreetmap.josm.tools.JosmRuntimeException;
|
|---|
| 10538 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 10539 |
|
|---|
| 10540 | +import java.awt.geom.Area;
|
|---|
| 10541 | +import java.awt.geom.Ellipse2D;
|
|---|
| 10542 | +import java.awt.geom.Path2D;
|
|---|
| 10543 | +import java.awt.geom.PathIterator;
|
|---|
| 10544 | +import java.util.ArrayList;
|
|---|
| 10545 | +import java.util.Arrays;
|
|---|
| 10546 | +import java.util.Collection;
|
|---|
| 10547 | +import java.util.Collections;
|
|---|
| 10548 | +import java.util.List;
|
|---|
| 10549 | +import java.util.Objects;
|
|---|
| 10550 | +import java.util.Optional;
|
|---|
| 10551 | +import java.util.stream.Collectors;
|
|---|
| 10552 | +
|
|---|
| 10553 |
|
|---|
| 10554 | /**
|
|---|
| 10555 | * A data store for Vector Data sets
|
|---|
| 10556 | --
|
|---|
| 10557 | GitLab
|
|---|
| 10558 |
|
|---|
| 10559 |
|
|---|
| 10560 | From f8f50fa8560899c79a6d76167d9d4e594644d4af Mon Sep 17 00:00:00 2001
|
|---|
| 10561 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10562 | Date: Thu, 22 Apr 2021 08:31:55 -0600
|
|---|
| 10563 | Subject: [PATCH 18/50] FIXUP: PMD
|
|---|
| 10564 |
|
|---|
| 10565 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10566 | ---
|
|---|
| 10567 | src/org/openstreetmap/josm/data/vector/VectorDataSet.java | 8 ++++----
|
|---|
| 10568 | .../openstreetmap/josm/data/vector/VectorDataStore.java | 8 +++++---
|
|---|
| 10569 | .../openstreetmap/josm/gui/layer/imagery/MVTLayer.java | 4 +++-
|
|---|
| 10570 | .../openstreetmap/josm/data/vector/VectorDataSetTest.java | 1 -
|
|---|
| 10571 | 4 files changed, 12 insertions(+), 9 deletions(-)
|
|---|
| 10572 |
|
|---|
| 10573 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 10574 | index 4e9cb9ffd..61eeb0279 100644
|
|---|
| 10575 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 10576 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 10577 | @@ -546,15 +546,15 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 10578 | * @param invisibleLayers The layer to not show
|
|---|
| 10579 | */
|
|---|
| 10580 | public void setInvisibleLayers(Collection<String> invisibleLayers) {
|
|---|
| 10581 | - if (invisibleLayers == null || invisibleLayers.isEmpty() || invisibleLayers.stream().filter(Objects::nonNull).filter(string -> !string.isEmpty()).count() == 0) {
|
|---|
| 10582 | + if (invisibleLayers == null || invisibleLayers.isEmpty()
|
|---|
| 10583 | + || invisibleLayers.stream().filter(Objects::nonNull).allMatch(String::isEmpty)) {
|
|---|
| 10584 | this.invisibleLayers = NO_INVISIBLE_LAYERS;
|
|---|
| 10585 | return;
|
|---|
| 10586 | }
|
|---|
| 10587 | String[] currentInvisibleLayers = invisibleLayers.stream().filter(Objects::nonNull).toArray(String[]::new);
|
|---|
| 10588 | this.invisibleLayers = currentInvisibleLayers;
|
|---|
| 10589 | List<String> temporaryList = Arrays.asList(currentInvisibleLayers);
|
|---|
| 10590 | - this.dataStoreMap.values().forEach(dataStore -> {
|
|---|
| 10591 | - dataStore.getAllPrimitives().parallelStream().forEach(primitive -> primitive.setVisible(!temporaryList.contains(primitive.getLayer())));
|
|---|
| 10592 | - });
|
|---|
| 10593 | + this.dataStoreMap.values().forEach(dataStore -> dataStore.getAllPrimitives().parallelStream()
|
|---|
| 10594 | + .forEach(primitive -> primitive.setVisible(!temporaryList.contains(primitive.getLayer()))));
|
|---|
| 10595 | }
|
|---|
| 10596 | }
|
|---|
| 10597 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10598 | index bd76e3538..7e48e4313 100644
|
|---|
| 10599 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10600 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 10601 | @@ -33,7 +33,6 @@ import java.util.Objects;
|
|---|
| 10602 | import java.util.Optional;
|
|---|
| 10603 | import java.util.stream.Collectors;
|
|---|
| 10604 |
|
|---|
| 10605 | -
|
|---|
| 10606 | /**
|
|---|
| 10607 | * A data store for Vector Data sets
|
|---|
| 10608 | * @author Taylor Smock
|
|---|
| 10609 | @@ -379,9 +378,12 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 10610 | }
|
|---|
| 10611 | // Replace original_ids with the same object (reduce memory usage)
|
|---|
| 10612 | // Strings aren't interned automatically (see
|
|---|
| 10613 | - Collection<IPrimitive> primitives = this.dataSet.allPrimitives().stream().filter(p -> p.hasKey(ORIGINAL_ID)).collect(Collectors.toList());
|
|---|
| 10614 | + Collection<IPrimitive> primitives = this.dataSet.allPrimitives().stream().filter(p -> p.hasKey(ORIGINAL_ID))
|
|---|
| 10615 | + .collect(Collectors.toList());
|
|---|
| 10616 | List<String> toReplace = primitives.stream().map(p -> p.get(ORIGINAL_ID)).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 10617 | - primitives.stream().filter(p -> toReplace.contains(p.get(ORIGINAL_ID))).forEach(p -> p.put(ORIGINAL_ID, toReplace.stream().filter(shared -> shared.equals(p.get(ORIGINAL_ID))).findAny().orElse(null)));
|
|---|
| 10618 | + primitives.stream().filter(p -> toReplace.contains(p.get(ORIGINAL_ID)))
|
|---|
| 10619 | + .forEach(p -> p.put(ORIGINAL_ID, toReplace.stream().filter(shared -> shared.equals(p.get(ORIGINAL_ID)))
|
|---|
| 10620 | + .findAny().orElse(null)));
|
|---|
| 10621 | }
|
|---|
| 10622 |
|
|---|
| 10623 | @Override
|
|---|
| 10624 | diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10625 | index 876ce5399..2e7aac3e6 100644
|
|---|
| 10626 | --- a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10627 | +++ b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 10628 | @@ -140,7 +140,9 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 10629 | actions.add(new EnableLayerAction(layerConfig.getKey(), () -> layerNames.computeIfAbsent(layerConfig.getKey(), key -> true),
|
|---|
| 10630 | layer -> {
|
|---|
| 10631 | layerNames.compute(layer, (key, value) -> Boolean.FALSE.equals(value));
|
|---|
| 10632 | - this.dataSet.setInvisibleLayers(layerNames.entrySet().stream().filter(entry -> Boolean.FALSE.equals(entry.getValue())).map(Map.Entry::getKey).collect(Collectors.toList()));
|
|---|
| 10633 | + this.dataSet.setInvisibleLayers(layerNames.entrySet().stream()
|
|---|
| 10634 | + .filter(entry -> Boolean.FALSE.equals(entry.getValue()))
|
|---|
| 10635 | + .map(Map.Entry::getKey).collect(Collectors.toList()));
|
|---|
| 10636 | this.invalidate();
|
|---|
| 10637 | }));
|
|---|
| 10638 | }
|
|---|
| 10639 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 10640 | index a33c396f4..35e208979 100644
|
|---|
| 10641 | --- a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 10642 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 10643 | @@ -15,7 +15,6 @@ import java.util.List;
|
|---|
| 10644 | import java.util.Map;
|
|---|
| 10645 | import java.util.stream.Collectors;
|
|---|
| 10646 |
|
|---|
| 10647 | -import org.junit.jupiter.api.RepeatedTest;
|
|---|
| 10648 | import org.openstreetmap.josm.TestUtils;
|
|---|
| 10649 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 10650 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 10651 | --
|
|---|
| 10652 | GitLab
|
|---|
| 10653 |
|
|---|
| 10654 |
|
|---|
| 10655 | From aafdee1cb7cd640b5980d5ccd280f713930d7758 Mon Sep 17 00:00:00 2001
|
|---|
| 10656 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10657 | Date: Thu, 22 Apr 2021 10:54:47 -0600
|
|---|
| 10658 | Subject: [PATCH 19/50] FIXUP: Failing tests (largely 2048->4096, extent is
|
|---|
| 10659 | uint not sint)
|
|---|
| 10660 |
|
|---|
| 10661 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10662 | ---
|
|---|
| 10663 | .../josm/data/imagery/vectortile/mapbox/LayerTest.java | 4 ++--
|
|---|
| 10664 | .../josm/data/imagery/vectortile/mapbox/MVTTileTest.java | 4 +---
|
|---|
| 10665 | .../josm/data/imagery/vectortile/mapbox/style/SourceTest.java | 3 ++-
|
|---|
| 10666 | 3 files changed, 5 insertions(+), 6 deletions(-)
|
|---|
| 10667 |
|
|---|
| 10668 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 10669 | index fc3ba9c27..61e21dc60 100644
|
|---|
| 10670 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 10671 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 10672 | @@ -79,14 +79,14 @@ public class LayerTest {
|
|---|
| 10673 | assertEquals("mapillary-sequences", sequenceLayer.getName());
|
|---|
| 10674 | assertEquals(1, sequenceLayer.getFeatures().size());
|
|---|
| 10675 | assertEquals(1, sequenceLayer.getGeometry().size());
|
|---|
| 10676 | - assertEquals(2048, sequenceLayer.getExtent());
|
|---|
| 10677 | + assertEquals(4096, sequenceLayer.getExtent());
|
|---|
| 10678 | assertEquals(1, sequenceLayer.getVersion());
|
|---|
| 10679 |
|
|---|
| 10680 | Layer imageLayer = new Layer(layers.get(1).getBytes());
|
|---|
| 10681 | assertEquals("mapillary-images", imageLayer.getName());
|
|---|
| 10682 | assertEquals(116, imageLayer.getFeatures().size());
|
|---|
| 10683 | assertEquals(116, imageLayer.getGeometry().size());
|
|---|
| 10684 | - assertEquals(2048, imageLayer.getExtent());
|
|---|
| 10685 | + assertEquals(4096, imageLayer.getExtent());
|
|---|
| 10686 | assertEquals(1, imageLayer.getVersion());
|
|---|
| 10687 | }
|
|---|
| 10688 |
|
|---|
| 10689 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 10690 | index 66e4ea781..12b86ebc7 100644
|
|---|
| 10691 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 10692 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 10693 | @@ -67,9 +67,7 @@ public class MVTTileTest {
|
|---|
| 10694 | if (isLoaded) {
|
|---|
| 10695 | Awaitility.await().atMost(Durations.ONE_SECOND).until(() -> tile.getLayers() != null && tile.getLayers().size() > 1);
|
|---|
| 10696 | assertEquals(2, tile.getLayers().size());
|
|---|
| 10697 | - // The test Mapillary tiles have 2048 instead of 4096 for their extent. This *may* change
|
|---|
| 10698 | - // in future Mapillary tiles, so if the test PBF files are updated, beware.
|
|---|
| 10699 | - assertEquals(2048, tile.getExtent());
|
|---|
| 10700 | + assertEquals(4096, tile.getExtent());
|
|---|
| 10701 | // Ensure that we have the clear image set, such that the tile doesn't add to the dataset again
|
|---|
| 10702 | // and we don't have a loading image
|
|---|
| 10703 | assertEquals(MVTTile.CLEAR_LOADED, tile.getImage());
|
|---|
| 10704 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java
|
|---|
| 10705 | index 500b5f8b5..8513be831 100644
|
|---|
| 10706 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java
|
|---|
| 10707 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceTest.java
|
|---|
| 10708 | @@ -20,6 +20,7 @@ import org.openstreetmap.josm.data.Bounds;
|
|---|
| 10709 |
|
|---|
| 10710 | import nl.jqno.equalsverifier.EqualsVerifier;
|
|---|
| 10711 | import org.junit.jupiter.api.Test;
|
|---|
| 10712 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.InvalidMapboxVectorTileException;
|
|---|
| 10713 |
|
|---|
| 10714 | /**
|
|---|
| 10715 | * Test class for {@link Source}
|
|---|
| 10716 | @@ -68,7 +69,7 @@ public class SourceTest {
|
|---|
| 10717 | final JsonObject tileJsonSpec = Json.createObjectBuilder()
|
|---|
| 10718 | .add("type", SourceType.VECTOR.name()).add("url", "some-random-url.com")
|
|---|
| 10719 | .build();
|
|---|
| 10720 | - assertThrows(UnsupportedOperationException.class, () -> new Source("Test TileJson", tileJsonSpec));
|
|---|
| 10721 | + assertThrows(InvalidMapboxVectorTileException.class, () -> new Source("Test TileJson", tileJsonSpec));
|
|---|
| 10722 | }
|
|---|
| 10723 |
|
|---|
| 10724 | @Test
|
|---|
| 10725 | --
|
|---|
| 10726 | GitLab
|
|---|
| 10727 |
|
|---|
| 10728 |
|
|---|
| 10729 | From d924f81efddc1da4c6b54977ece1c76aa2862d6d Mon Sep 17 00:00:00 2001
|
|---|
| 10730 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 10731 | Date: Tue, 27 Apr 2021 08:39:14 -0600
|
|---|
| 10732 | Subject: [PATCH 20/50] VectorDataSet: Add selection listener interface
|
|---|
| 10733 |
|
|---|
| 10734 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 10735 | ---
|
|---|
| 10736 | .../osm/event/IDataSelectionEventSource.java | 34 ++
|
|---|
| 10737 | .../osm/event/IDataSelectionListener.java | 361 ++++++++++++++++++
|
|---|
| 10738 | .../josm/data/vector/VectorDataSet.java | 88 ++++-
|
|---|
| 10739 | 3 files changed, 466 insertions(+), 17 deletions(-)
|
|---|
| 10740 | create mode 100644 src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 10741 | create mode 100644 src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java
|
|---|
| 10742 |
|
|---|
| 10743 | diff --git a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 10744 | new file mode 100644
|
|---|
| 10745 | index 000000000..e5fc0ea19
|
|---|
| 10746 | --- /dev/null
|
|---|
| 10747 | +++ b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 10748 | @@ -0,0 +1,34 @@
|
|---|
| 10749 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 10750 | +package org.openstreetmap.josm.data.osm.event;
|
|---|
| 10751 | +
|
|---|
| 10752 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 10753 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 10754 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 10755 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 10756 | +import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 10757 | +
|
|---|
| 10758 | +/**
|
|---|
| 10759 | + * This interface indicates that the class can fire {@link IDataSelectionListener}.
|
|---|
| 10760 | + * @author Taylor Smock, Michael Zangl (original code)
|
|---|
| 10761 | + * @since xxx
|
|---|
| 10762 | + * @param <O> the base type of OSM primitives
|
|---|
| 10763 | + * @param <N> type representing OSM nodes
|
|---|
| 10764 | + * @param <W> type representing OSM ways
|
|---|
| 10765 | + * @param <R> type representing OSM relations
|
|---|
| 10766 | + * @param <D> The dataset type
|
|---|
| 10767 | + */
|
|---|
| 10768 | +public interface IDataSelectionEventSource<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> {
|
|---|
| 10769 | + /**
|
|---|
| 10770 | + * Add a listener
|
|---|
| 10771 | + * @param listener The listener to add
|
|---|
| 10772 | + * @return {@code true} if the listener was added
|
|---|
| 10773 | + */
|
|---|
| 10774 | + boolean addListener(IDataSelectionListener<O, N, W, R, D> listener);
|
|---|
| 10775 | +
|
|---|
| 10776 | + /**
|
|---|
| 10777 | + * Remove a listener
|
|---|
| 10778 | + * @param listener The listener to remove
|
|---|
| 10779 | + * @return {@code true} if the listener was removed
|
|---|
| 10780 | + */
|
|---|
| 10781 | + boolean removeListener(IDataSelectionListener<O, N, W, R, D> listener);
|
|---|
| 10782 | +}
|
|---|
| 10783 | diff --git a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java
|
|---|
| 10784 | new file mode 100644
|
|---|
| 10785 | index 000000000..5a95aa1c9
|
|---|
| 10786 | --- /dev/null
|
|---|
| 10787 | +++ b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java
|
|---|
| 10788 | @@ -0,0 +1,361 @@
|
|---|
| 10789 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 10790 | +package org.openstreetmap.josm.data.osm.event;
|
|---|
| 10791 | +
|
|---|
| 10792 | +import org.openstreetmap.josm.data.osm.DataSelectionListener;
|
|---|
| 10793 | +import org.openstreetmap.josm.data.osm.INode;
|
|---|
| 10794 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 10795 | +import org.openstreetmap.josm.data.osm.IRelation;
|
|---|
| 10796 | +import org.openstreetmap.josm.data.osm.IWay;
|
|---|
| 10797 | +import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 10798 | +import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
|---|
| 10799 | +import org.openstreetmap.josm.tools.CheckParameterUtil;
|
|---|
| 10800 | +
|
|---|
| 10801 | +import java.util.Collections;
|
|---|
| 10802 | +import java.util.HashSet;
|
|---|
| 10803 | +import java.util.LinkedHashSet;
|
|---|
| 10804 | +import java.util.Set;
|
|---|
| 10805 | +import java.util.stream.Collectors;
|
|---|
| 10806 | +import java.util.stream.Stream;
|
|---|
| 10807 | +
|
|---|
| 10808 | +/**
|
|---|
| 10809 | + * This interface is the same as {@link DataSelectionListener}, except it isn't {@link OsmPrimitive} specific.
|
|---|
| 10810 | + * @author Taylor Smock, Michael Zangl (original code)
|
|---|
| 10811 | + * @since xxx
|
|---|
| 10812 | + * @param <O> the base type of OSM primitives
|
|---|
| 10813 | + * @param <N> type representing OSM nodes
|
|---|
| 10814 | + * @param <W> type representing OSM ways
|
|---|
| 10815 | + * @param <R> type representing OSM relations
|
|---|
| 10816 | + * @param <D> The dataset type
|
|---|
| 10817 | + */
|
|---|
| 10818 | +@FunctionalInterface
|
|---|
| 10819 | +public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> {
|
|---|
| 10820 | + /**
|
|---|
| 10821 | + * Called whenever the selection is changed.
|
|---|
| 10822 | + *
|
|---|
| 10823 | + * You get notified about the new selection, the elements that were added and removed and the layer that triggered the event.
|
|---|
| 10824 | + * @param event The selection change event.
|
|---|
| 10825 | + * @see SelectionChangeEvent
|
|---|
| 10826 | + */
|
|---|
| 10827 | + void selectionChanged(SelectionChangeEvent<O, N, W, R, D> event);
|
|---|
| 10828 | +
|
|---|
| 10829 | + /**
|
|---|
| 10830 | + * The event that is fired when the selection changed.
|
|---|
| 10831 | + * @author Michael Zangl
|
|---|
| 10832 | + * @since xxx generics
|
|---|
| 10833 | + * @param <O> the base type of OSM primitives
|
|---|
| 10834 | + * @param <N> type representing OSM nodes
|
|---|
| 10835 | + * @param <W> type representing OSM ways
|
|---|
| 10836 | + * @param <R> type representing OSM relations
|
|---|
| 10837 | + * @param <D> The dataset type
|
|---|
| 10838 | + */
|
|---|
| 10839 | + interface SelectionChangeEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> {
|
|---|
| 10840 | + /**
|
|---|
| 10841 | + * Gets the previous selection
|
|---|
| 10842 | + * <p>
|
|---|
| 10843 | + * This collection cannot be modified and will not change.
|
|---|
| 10844 | + * @return The old selection
|
|---|
| 10845 | + */
|
|---|
| 10846 | + Set<O> getOldSelection();
|
|---|
| 10847 | +
|
|---|
| 10848 | + /**
|
|---|
| 10849 | + * Gets the new selection. New elements are added to the end of the collection.
|
|---|
| 10850 | + * <p>
|
|---|
| 10851 | + * This collection cannot be modified and will not change.
|
|---|
| 10852 | + * @return The new selection
|
|---|
| 10853 | + */
|
|---|
| 10854 | + Set<O> getSelection();
|
|---|
| 10855 | +
|
|---|
| 10856 | + /**
|
|---|
| 10857 | + * Gets the primitives that have been removed from the selection.
|
|---|
| 10858 | + * <p>
|
|---|
| 10859 | + * Those are the primitives contained in {@link #getOldSelection()} but not in {@link #getSelection()}
|
|---|
| 10860 | + * <p>
|
|---|
| 10861 | + * This collection cannot be modified and will not change.
|
|---|
| 10862 | + * @return The primitives that were removed
|
|---|
| 10863 | + */
|
|---|
| 10864 | + Set<O> getRemoved();
|
|---|
| 10865 | +
|
|---|
| 10866 | + /**
|
|---|
| 10867 | + * Gets the primitives that have been added to the selection.
|
|---|
| 10868 | + * <p>
|
|---|
| 10869 | + * Those are the primitives contained in {@link #getSelection()} but not in {@link #getOldSelection()}
|
|---|
| 10870 | + * <p>
|
|---|
| 10871 | + * This collection cannot be modified and will not change.
|
|---|
| 10872 | + * @return The primitives that were added
|
|---|
| 10873 | + */
|
|---|
| 10874 | + Set<O> getAdded();
|
|---|
| 10875 | +
|
|---|
| 10876 | + /**
|
|---|
| 10877 | + * Gets the data set that triggered this selection event.
|
|---|
| 10878 | + * @return The data set.
|
|---|
| 10879 | + */
|
|---|
| 10880 | + D getSource();
|
|---|
| 10881 | +
|
|---|
| 10882 | + /**
|
|---|
| 10883 | + * Test if this event did not change anything.
|
|---|
| 10884 | + * <p>
|
|---|
| 10885 | + * This will return <code>false</code> for all events that are sent to listeners, so you don't need to test it.
|
|---|
| 10886 | + * @return <code>true</code> if this did not change the selection.
|
|---|
| 10887 | + */
|
|---|
| 10888 | + default boolean isNop() {
|
|---|
| 10889 | + return getAdded().isEmpty() && getRemoved().isEmpty();
|
|---|
| 10890 | + }
|
|---|
| 10891 | + }
|
|---|
| 10892 | +
|
|---|
| 10893 | + /**
|
|---|
| 10894 | + * The base class for selection events
|
|---|
| 10895 | + * @author Michael Zangl
|
|---|
| 10896 | + * @since 12048, xxx (generics)
|
|---|
| 10897 | + * @param <O> the base type of OSM primitives
|
|---|
| 10898 | + * @param <N> type representing OSM nodes
|
|---|
| 10899 | + * @param <W> type representing OSM ways
|
|---|
| 10900 | + * @param <R> type representing OSM relations
|
|---|
| 10901 | + * @param <D> The dataset type
|
|---|
| 10902 | + */
|
|---|
| 10903 | + abstract class AbstractSelectionEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> implements SelectionChangeEvent<O, N, W, R, D> {
|
|---|
| 10904 | + private final D source;
|
|---|
| 10905 | + private final Set<O> old;
|
|---|
| 10906 | +
|
|---|
| 10907 | + protected AbstractSelectionEvent(D source, Set<O> old) {
|
|---|
| 10908 | + CheckParameterUtil.ensureParameterNotNull(source, "source");
|
|---|
| 10909 | + CheckParameterUtil.ensureParameterNotNull(old, "old");
|
|---|
| 10910 | + this.source = source;
|
|---|
| 10911 | + this.old = Collections.unmodifiableSet(old);
|
|---|
| 10912 | + }
|
|---|
| 10913 | +
|
|---|
| 10914 | + @Override
|
|---|
| 10915 | + public Set<O> getOldSelection() {
|
|---|
| 10916 | + return old;
|
|---|
| 10917 | + }
|
|---|
| 10918 | +
|
|---|
| 10919 | + @Override
|
|---|
| 10920 | + public D getSource() {
|
|---|
| 10921 | + return source;
|
|---|
| 10922 | + }
|
|---|
| 10923 | + }
|
|---|
| 10924 | +
|
|---|
| 10925 | + /**
|
|---|
| 10926 | + * The selection is replaced by a new selection
|
|---|
| 10927 | + * @author Michael Zangl
|
|---|
| 10928 | + * @since xxx (generics)
|
|---|
| 10929 | + * @param <O> the base type of OSM primitives
|
|---|
| 10930 | + * @param <N> type representing OSM nodes
|
|---|
| 10931 | + * @param <W> type representing OSM ways
|
|---|
| 10932 | + * @param <R> type representing OSM relations
|
|---|
| 10933 | + * @param <D> The dataset type
|
|---|
| 10934 | + */
|
|---|
| 10935 | + class SelectionReplaceEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 10936 | + private final Set<O> current;
|
|---|
| 10937 | + private Set<O> removed;
|
|---|
| 10938 | + private Set<O> added;
|
|---|
| 10939 | +
|
|---|
| 10940 | + /**
|
|---|
| 10941 | + * Create a {@link SelectionReplaceEvent}
|
|---|
| 10942 | + * @param source The source dataset
|
|---|
| 10943 | + * @param old The old primitives that were previously selected. The caller needs to ensure that this set is not modified.
|
|---|
| 10944 | + * @param newSelection The primitives of the new selection.
|
|---|
| 10945 | + */
|
|---|
| 10946 | + public SelectionReplaceEvent(D source, Set<O> old, Stream<O> newSelection) {
|
|---|
| 10947 | + super(source, old);
|
|---|
| 10948 | + this.current = newSelection.collect(Collectors.toCollection(LinkedHashSet::new));
|
|---|
| 10949 | + }
|
|---|
| 10950 | +
|
|---|
| 10951 | + @Override
|
|---|
| 10952 | + public Set<O> getSelection() {
|
|---|
| 10953 | + return current;
|
|---|
| 10954 | + }
|
|---|
| 10955 | +
|
|---|
| 10956 | + @Override
|
|---|
| 10957 | + public synchronized Set<O> getRemoved() {
|
|---|
| 10958 | + if (removed == null) {
|
|---|
| 10959 | + removed = getOldSelection().stream()
|
|---|
| 10960 | + .filter(p -> !current.contains(p))
|
|---|
| 10961 | + .collect(Collectors.toCollection(LinkedHashSet::new));
|
|---|
| 10962 | + }
|
|---|
| 10963 | + return removed;
|
|---|
| 10964 | + }
|
|---|
| 10965 | +
|
|---|
| 10966 | + @Override
|
|---|
| 10967 | + public synchronized Set<O> getAdded() {
|
|---|
| 10968 | + if (added == null) {
|
|---|
| 10969 | + added = current.stream()
|
|---|
| 10970 | + .filter(p -> !getOldSelection().contains(p)).collect(Collectors.toCollection(LinkedHashSet::new));
|
|---|
| 10971 | + }
|
|---|
| 10972 | + return added;
|
|---|
| 10973 | + }
|
|---|
| 10974 | +
|
|---|
| 10975 | + @Override
|
|---|
| 10976 | + public String toString() {
|
|---|
| 10977 | + return "SelectionReplaceEvent [current=" + current + ", removed=" + removed + ", added=" + added + ']';
|
|---|
| 10978 | + }
|
|---|
| 10979 | + }
|
|---|
| 10980 | +
|
|---|
| 10981 | + /**
|
|---|
| 10982 | + * Primitives are added to the selection
|
|---|
| 10983 | + * @author Michael Zangl
|
|---|
| 10984 | + * @since xxx (generics)
|
|---|
| 10985 | + * @param <O> the base type of OSM primitives
|
|---|
| 10986 | + * @param <N> type representing OSM nodes
|
|---|
| 10987 | + * @param <W> type representing OSM ways
|
|---|
| 10988 | + * @param <R> type representing OSM relations
|
|---|
| 10989 | + * @param <D> The dataset type
|
|---|
| 10990 | + */
|
|---|
| 10991 | + class SelectionAddEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 10992 | + private final Set<O> add;
|
|---|
| 10993 | + private final Set<O> current;
|
|---|
| 10994 | +
|
|---|
| 10995 | + /**
|
|---|
| 10996 | + * Create a {@link SelectionAddEvent}
|
|---|
| 10997 | + * @param source The source dataset
|
|---|
| 10998 | + * @param old The old primitives that were previously selected. The caller needs to ensure that this set is not modified.
|
|---|
| 10999 | + * @param toAdd The primitives to add.
|
|---|
| 11000 | + */
|
|---|
| 11001 | + public SelectionAddEvent(D source, Set<O> old, Stream<O> toAdd) {
|
|---|
| 11002 | + super(source, old);
|
|---|
| 11003 | + this.add = toAdd
|
|---|
| 11004 | + .filter(p -> !old.contains(p))
|
|---|
| 11005 | + .collect(Collectors.toCollection(LinkedHashSet::new));
|
|---|
| 11006 | + if (this.add.isEmpty()) {
|
|---|
| 11007 | + this.current = this.getOldSelection();
|
|---|
| 11008 | + } else {
|
|---|
| 11009 | + this.current = new LinkedHashSet<>(old);
|
|---|
| 11010 | + this.current.addAll(add);
|
|---|
| 11011 | + }
|
|---|
| 11012 | + }
|
|---|
| 11013 | +
|
|---|
| 11014 | + @Override
|
|---|
| 11015 | + public Set<O> getSelection() {
|
|---|
| 11016 | + return Collections.unmodifiableSet(current);
|
|---|
| 11017 | + }
|
|---|
| 11018 | +
|
|---|
| 11019 | + @Override
|
|---|
| 11020 | + public Set<O> getRemoved() {
|
|---|
| 11021 | + return Collections.emptySet();
|
|---|
| 11022 | + }
|
|---|
| 11023 | +
|
|---|
| 11024 | + @Override
|
|---|
| 11025 | + public Set<O> getAdded() {
|
|---|
| 11026 | + return Collections.unmodifiableSet(add);
|
|---|
| 11027 | + }
|
|---|
| 11028 | +
|
|---|
| 11029 | + @Override
|
|---|
| 11030 | + public String toString() {
|
|---|
| 11031 | + return "SelectionAddEvent [add=" + add + ", current=" + current + ']';
|
|---|
| 11032 | + }
|
|---|
| 11033 | + }
|
|---|
| 11034 | +
|
|---|
| 11035 | + /**
|
|---|
| 11036 | + * Primitives are removed from the selection
|
|---|
| 11037 | + * @author Michael Zangl
|
|---|
| 11038 | + * @since 12048, xxx (generics)
|
|---|
| 11039 | + * @param <O> the base type of OSM primitives
|
|---|
| 11040 | + * @param <N> type representing OSM nodes
|
|---|
| 11041 | + * @param <W> type representing OSM ways
|
|---|
| 11042 | + * @param <R> type representing OSM relations
|
|---|
| 11043 | + * @param <D> The dataset type
|
|---|
| 11044 | + */
|
|---|
| 11045 | + class SelectionRemoveEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11046 | + private final Set<O> remove;
|
|---|
| 11047 | + private final Set<O> current;
|
|---|
| 11048 | +
|
|---|
| 11049 | + /**
|
|---|
| 11050 | + * Create a {@link DataSelectionListener.SelectionRemoveEvent}
|
|---|
| 11051 | + * @param source The source dataset
|
|---|
| 11052 | + * @param old The old primitives that were previously selected. The caller needs to ensure that this set is not modified.
|
|---|
| 11053 | + * @param toRemove The primitives to remove.
|
|---|
| 11054 | + */
|
|---|
| 11055 | + public SelectionRemoveEvent(D source, Set<O> old, Stream<O> toRemove) {
|
|---|
| 11056 | + super(source, old);
|
|---|
| 11057 | + this.remove = toRemove
|
|---|
| 11058 | + .filter(old::contains)
|
|---|
| 11059 | + .collect(Collectors.toCollection(LinkedHashSet::new));
|
|---|
| 11060 | + if (this.remove.isEmpty()) {
|
|---|
| 11061 | + this.current = this.getOldSelection();
|
|---|
| 11062 | + } else {
|
|---|
| 11063 | + HashSet<O> currentSet = new LinkedHashSet<>(old);
|
|---|
| 11064 | + currentSet.removeAll(remove);
|
|---|
| 11065 | + current = currentSet;
|
|---|
| 11066 | + }
|
|---|
| 11067 | + }
|
|---|
| 11068 | +
|
|---|
| 11069 | + @Override
|
|---|
| 11070 | + public Set<O> getSelection() {
|
|---|
| 11071 | + return Collections.unmodifiableSet(current);
|
|---|
| 11072 | + }
|
|---|
| 11073 | +
|
|---|
| 11074 | + @Override
|
|---|
| 11075 | + public Set<O> getRemoved() {
|
|---|
| 11076 | + return Collections.unmodifiableSet(remove);
|
|---|
| 11077 | + }
|
|---|
| 11078 | +
|
|---|
| 11079 | + @Override
|
|---|
| 11080 | + public Set<O> getAdded() {
|
|---|
| 11081 | + return Collections.emptySet();
|
|---|
| 11082 | + }
|
|---|
| 11083 | +
|
|---|
| 11084 | + @Override
|
|---|
| 11085 | + public String toString() {
|
|---|
| 11086 | + return "SelectionRemoveEvent [remove=" + remove + ", current=" + current + ']';
|
|---|
| 11087 | + }
|
|---|
| 11088 | + }
|
|---|
| 11089 | +
|
|---|
| 11090 | + /**
|
|---|
| 11091 | + * Toggle the selected state of a primitive
|
|---|
| 11092 | + * @author Michael Zangl
|
|---|
| 11093 | + * @since xxx (generics)
|
|---|
| 11094 | + * @param <O> the base type of OSM primitives
|
|---|
| 11095 | + * @param <N> type representing OSM nodes
|
|---|
| 11096 | + * @param <W> type representing OSM ways
|
|---|
| 11097 | + * @param <R> type representing OSM relations
|
|---|
| 11098 | + * @param <D> The dataset type
|
|---|
| 11099 | + */
|
|---|
| 11100 | + class SelectionToggleEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11101 | + private final Set<O> current;
|
|---|
| 11102 | + private final Set<O> remove;
|
|---|
| 11103 | + private final Set<O> add;
|
|---|
| 11104 | +
|
|---|
| 11105 | + /**
|
|---|
| 11106 | + * Create a {@link SelectionToggleEvent}
|
|---|
| 11107 | + * @param source The source dataset
|
|---|
| 11108 | + * @param old The old primitives that were previously selected. The caller needs to ensure that this set is not modified.
|
|---|
| 11109 | + * @param toToggle The primitives to toggle.
|
|---|
| 11110 | + */
|
|---|
| 11111 | + public SelectionToggleEvent(D source, Set<O> old, Stream<O> toToggle) {
|
|---|
| 11112 | + super(source, old);
|
|---|
| 11113 | + HashSet<O> currentSet = new LinkedHashSet<>(old);
|
|---|
| 11114 | + HashSet<O> removeSet = new LinkedHashSet<>();
|
|---|
| 11115 | + HashSet<O> addSet = new LinkedHashSet<>();
|
|---|
| 11116 | + toToggle.forEach(p -> {
|
|---|
| 11117 | + if (currentSet.remove(p)) {
|
|---|
| 11118 | + removeSet.add(p);
|
|---|
| 11119 | + } else {
|
|---|
| 11120 | + addSet.add(p);
|
|---|
| 11121 | + currentSet.add(p);
|
|---|
| 11122 | + }
|
|---|
| 11123 | + });
|
|---|
| 11124 | + this.current = Collections.unmodifiableSet(currentSet);
|
|---|
| 11125 | + this.remove = Collections.unmodifiableSet(removeSet);
|
|---|
| 11126 | + this.add = Collections.unmodifiableSet(addSet);
|
|---|
| 11127 | + }
|
|---|
| 11128 | +
|
|---|
| 11129 | + @Override
|
|---|
| 11130 | + public Set<O> getSelection() {
|
|---|
| 11131 | + return current;
|
|---|
| 11132 | + }
|
|---|
| 11133 | +
|
|---|
| 11134 | + @Override
|
|---|
| 11135 | + public Set<O> getRemoved() {
|
|---|
| 11136 | + return remove;
|
|---|
| 11137 | + }
|
|---|
| 11138 | +
|
|---|
| 11139 | + @Override
|
|---|
| 11140 | + public Set<O> getAdded() {
|
|---|
| 11141 | + return add;
|
|---|
| 11142 | + }
|
|---|
| 11143 | +
|
|---|
| 11144 | + @Override
|
|---|
| 11145 | + public String toString() {
|
|---|
| 11146 | + return "SelectionToggleEvent [current=" + current + ", remove=" + remove + ", add=" + add + ']';
|
|---|
| 11147 | + }
|
|---|
| 11148 | + }
|
|---|
| 11149 | +}
|
|---|
| 11150 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11151 | index 61eeb0279..229fdf40e 100644
|
|---|
| 11152 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11153 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11154 | @@ -6,13 +6,16 @@ import java.util.Arrays;
|
|---|
| 11155 | import java.util.Collection;
|
|---|
| 11156 | import java.util.Collections;
|
|---|
| 11157 | import java.util.HashSet;
|
|---|
| 11158 | +import java.util.LinkedHashSet;
|
|---|
| 11159 | import java.util.List;
|
|---|
| 11160 | import java.util.Map;
|
|---|
| 11161 | import java.util.Objects;
|
|---|
| 11162 | import java.util.Optional;
|
|---|
| 11163 | +import java.util.Set;
|
|---|
| 11164 | import java.util.concurrent.ConcurrentHashMap;
|
|---|
| 11165 | import java.util.concurrent.locks.Lock;
|
|---|
| 11166 | import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|---|
| 11167 | +import java.util.function.Function;
|
|---|
| 11168 | import java.util.function.Predicate;
|
|---|
| 11169 | import java.util.function.Supplier;
|
|---|
| 11170 | import java.util.stream.Collectors;
|
|---|
| 11171 | @@ -24,12 +27,17 @@ import org.openstreetmap.josm.data.DataSource;
|
|---|
| 11172 | import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 11173 | import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 11174 | import org.openstreetmap.josm.data.osm.DataSelectionListener;
|
|---|
| 11175 | +import org.openstreetmap.josm.data.osm.DataSet;
|
|---|
| 11176 | import org.openstreetmap.josm.data.osm.DownloadPolicy;
|
|---|
| 11177 | import org.openstreetmap.josm.data.osm.HighlightUpdateListener;
|
|---|
| 11178 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 11179 | import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 11180 | +import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
|---|
| 11181 | import org.openstreetmap.josm.data.osm.PrimitiveId;
|
|---|
| 11182 | import org.openstreetmap.josm.data.osm.UploadPolicy;
|
|---|
| 11183 | import org.openstreetmap.josm.data.osm.WaySegment;
|
|---|
| 11184 | +import org.openstreetmap.josm.data.osm.event.IDataSelectionEventSource;
|
|---|
| 11185 | +import org.openstreetmap.josm.data.osm.event.IDataSelectionListener;
|
|---|
| 11186 | import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 11187 | import org.openstreetmap.josm.tools.ListenerList;
|
|---|
| 11188 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 11189 | @@ -41,11 +49,10 @@ import org.openstreetmap.josm.tools.SubclassFilteredCollection;
|
|---|
| 11190 | * @author Taylor Smock
|
|---|
| 11191 | * @since xxx
|
|---|
| 11192 | */
|
|---|
| 11193 | -public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, VectorWay, VectorRelation> {
|
|---|
| 11194 | +public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, VectorWay, VectorRelation>, IDataSelectionEventSource<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> {
|
|---|
| 11195 | // Note: In Java 8, computeIfAbsent is blocking for both pre-existing and new values. In Java 9, it is only blocking
|
|---|
| 11196 | // for new values (perf increase). See JDK-8161372 for more info.
|
|---|
| 11197 | private final Map<Integer, VectorDataStore> dataStoreMap = new ConcurrentHashMap<>();
|
|---|
| 11198 | - private final Collection<PrimitiveId> selected = new HashSet<>();
|
|---|
| 11199 | // Both of these listener lists are useless, since they expect OsmPrimitives at this time
|
|---|
| 11200 | private final ListenerList<HighlightUpdateListener> highlightUpdateListenerListenerList = ListenerList.create();
|
|---|
| 11201 | private final ListenerList<DataSelectionListener> dataSelectionListenerListenerList = ListenerList.create();
|
|---|
| 11202 | @@ -55,6 +62,17 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11203 | private String name;
|
|---|
| 11204 | private short mappaintCacheIdx = 1;
|
|---|
| 11205 |
|
|---|
| 11206 | + private final Object selectionLock = new Object();
|
|---|
| 11207 | + /**
|
|---|
| 11208 | + * The current selected primitives. This is always a unmodifiable set.
|
|---|
| 11209 | + *
|
|---|
| 11210 | + * The set should be ordered in the order in which the primitives have been added to the selection.
|
|---|
| 11211 | + */
|
|---|
| 11212 | + private Set<PrimitiveId> currentSelectedPrimitives = Collections.emptySet();
|
|---|
| 11213 | +
|
|---|
| 11214 | + private final ListenerList<IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet>> listeners =
|
|---|
| 11215 | + ListenerList.create();
|
|---|
| 11216 | +
|
|---|
| 11217 | private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
|---|
| 11218 |
|
|---|
| 11219 | /**
|
|---|
| 11220 | @@ -298,7 +316,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11221 | public Collection<VectorPrimitive> getAllSelected() {
|
|---|
| 11222 | final Optional<VectorDataStore> dataStore = this.getBestZoomDataStore();
|
|---|
| 11223 | return dataStore.map(vectorDataStore -> vectorDataStore.getAllPrimitives().stream()
|
|---|
| 11224 | - .filter(primitive -> this.selected.contains(primitive.getPrimitiveId()))
|
|---|
| 11225 | + .filter(primitive -> this.currentSelectedPrimitives.contains(primitive.getPrimitiveId()))
|
|---|
| 11226 | .collect(Collectors.toList())).orElse(Collections.emptyList());
|
|---|
| 11227 | }
|
|---|
| 11228 |
|
|---|
| 11229 | @@ -334,12 +352,12 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11230 |
|
|---|
| 11231 | @Override
|
|---|
| 11232 | public boolean selectionEmpty() {
|
|---|
| 11233 | - return this.selected.isEmpty();
|
|---|
| 11234 | + return this.currentSelectedPrimitives.isEmpty();
|
|---|
| 11235 | }
|
|---|
| 11236 |
|
|---|
| 11237 | @Override
|
|---|
| 11238 | public boolean isSelected(VectorPrimitive osm) {
|
|---|
| 11239 | - return this.selected.contains(osm.getPrimitiveId());
|
|---|
| 11240 | + return this.currentSelectedPrimitives.contains(osm.getPrimitiveId());
|
|---|
| 11241 | }
|
|---|
| 11242 |
|
|---|
| 11243 | @Override
|
|---|
| 11244 | @@ -353,13 +371,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11245 | }
|
|---|
| 11246 |
|
|---|
| 11247 | private void toggleSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 11248 | - osm.forEach(primitiveId -> {
|
|---|
| 11249 | - if (this.selected.contains(primitiveId)) {
|
|---|
| 11250 | - this.selected.remove(primitiveId);
|
|---|
| 11251 | - } else {
|
|---|
| 11252 | - this.selected.add(primitiveId);
|
|---|
| 11253 | - }
|
|---|
| 11254 | - });
|
|---|
| 11255 | + this.doSelectionChange(old -> new IDataSelectionListener.SelectionToggleEvent<>(this, old,
|
|---|
| 11256 | + osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 11257 | }
|
|---|
| 11258 |
|
|---|
| 11259 | @Override
|
|---|
| 11260 | @@ -373,8 +386,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11261 | }
|
|---|
| 11262 |
|
|---|
| 11263 | private void setSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 11264 | - this.selected.clear();
|
|---|
| 11265 | - osm.forEach(this.selected::add);
|
|---|
| 11266 | + this.doSelectionChange(old -> new IDataSelectionListener.SelectionReplaceEvent<>(this, old,
|
|---|
| 11267 | + osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 11268 | }
|
|---|
| 11269 |
|
|---|
| 11270 | @Override
|
|---|
| 11271 | @@ -388,7 +401,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11272 | }
|
|---|
| 11273 |
|
|---|
| 11274 | private void addSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 11275 | - osm.forEach(this.selected::add);
|
|---|
| 11276 | + this.doSelectionChange(old -> new IDataSelectionListener.SelectionAddEvent<>(this, old,
|
|---|
| 11277 | + osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 11278 | }
|
|---|
| 11279 |
|
|---|
| 11280 | @Override
|
|---|
| 11281 | @@ -403,11 +417,35 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11282 |
|
|---|
| 11283 | @Override
|
|---|
| 11284 | public void clearSelection() {
|
|---|
| 11285 | - this.clearSelectionImpl(new ArrayList<>(this.selected).stream());
|
|---|
| 11286 | + this.clearSelectionImpl(new ArrayList<>(this.currentSelectedPrimitives).stream());
|
|---|
| 11287 | }
|
|---|
| 11288 |
|
|---|
| 11289 | private void clearSelectionImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 11290 | - osm.forEach(this.selected::remove);
|
|---|
| 11291 | + this.doSelectionChange(old -> new IDataSelectionListener.SelectionRemoveEvent<>(this, old,
|
|---|
| 11292 | + osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 11293 | + }
|
|---|
| 11294 | +
|
|---|
| 11295 | + /**
|
|---|
| 11296 | + * Do a selection change.
|
|---|
| 11297 | + * <p>
|
|---|
| 11298 | + * This is the only method that changes the current selection state.
|
|---|
| 11299 | + * @param command A generator that generates the {@link DataSelectionListener.SelectionChangeEvent}
|
|---|
| 11300 | + * for the given base set of currently selected primitives.
|
|---|
| 11301 | + * @return true iff the command did change the selection.
|
|---|
| 11302 | + */
|
|---|
| 11303 | + private boolean doSelectionChange(final Function<Set<VectorPrimitive>,
|
|---|
| 11304 | + IDataSelectionListener.SelectionChangeEvent<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet>> command) {
|
|---|
| 11305 | + synchronized (this.selectionLock) {
|
|---|
| 11306 | + IDataSelectionListener.SelectionChangeEvent<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> event =
|
|---|
| 11307 | + command.apply(currentSelectedPrimitives.stream().map(this::getPrimitiveById).collect(Collectors.toSet()));
|
|---|
| 11308 | + if (event.isNop()) {
|
|---|
| 11309 | + return false;
|
|---|
| 11310 | + }
|
|---|
| 11311 | + this.currentSelectedPrimitives = event.getSelection().stream().map(IPrimitive::getPrimitiveId)
|
|---|
| 11312 | + .collect(Collectors.toCollection(LinkedHashSet::new));
|
|---|
| 11313 | + this.listeners.fireEvent(l -> l.selectionChanged(event));
|
|---|
| 11314 | + return true;
|
|---|
| 11315 | + }
|
|---|
| 11316 | }
|
|---|
| 11317 |
|
|---|
| 11318 | @Override
|
|---|
| 11319 | @@ -557,4 +595,20 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11320 | this.dataStoreMap.values().forEach(dataStore -> dataStore.getAllPrimitives().parallelStream()
|
|---|
| 11321 | .forEach(primitive -> primitive.setVisible(!temporaryList.contains(primitive.getLayer()))));
|
|---|
| 11322 | }
|
|---|
| 11323 | +
|
|---|
| 11324 | + @Override
|
|---|
| 11325 | + public boolean addListener(IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 11326 | + if (!this.listeners.containsListener(listener)) {
|
|---|
| 11327 | + this.listeners.addListener(listener);
|
|---|
| 11328 | + }
|
|---|
| 11329 | + return this.listeners.containsListener(listener);
|
|---|
| 11330 | + }
|
|---|
| 11331 | +
|
|---|
| 11332 | + @Override
|
|---|
| 11333 | + public boolean removeListener(IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 11334 | + if (this.listeners.containsListener(listener)) {
|
|---|
| 11335 | + this.listeners.removeListener(listener);
|
|---|
| 11336 | + }
|
|---|
| 11337 | + return this.listeners.containsListener(listener);
|
|---|
| 11338 | + }
|
|---|
| 11339 | }
|
|---|
| 11340 | --
|
|---|
| 11341 | GitLab
|
|---|
| 11342 |
|
|---|
| 11343 |
|
|---|
| 11344 | From 46085d1dadda30d4b690776b9ffb02932aa85c0a Mon Sep 17 00:00:00 2001
|
|---|
| 11345 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 11346 | Date: Tue, 27 Apr 2021 16:31:32 -0600
|
|---|
| 11347 | Subject: [PATCH 21/50] BBox: addPrimitive: Overload so that IPrimitives work
|
|---|
| 11348 |
|
|---|
| 11349 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 11350 | ---
|
|---|
| 11351 | src/org/openstreetmap/josm/data/osm/BBox.java | 10 ++++++++++
|
|---|
| 11352 | 1 file changed, 10 insertions(+)
|
|---|
| 11353 |
|
|---|
| 11354 | diff --git a/src/org/openstreetmap/josm/data/osm/BBox.java b/src/org/openstreetmap/josm/data/osm/BBox.java
|
|---|
| 11355 | index e16f984d1..7dcc79a41 100644
|
|---|
| 11356 | --- a/src/org/openstreetmap/josm/data/osm/BBox.java
|
|---|
| 11357 | +++ b/src/org/openstreetmap/josm/data/osm/BBox.java
|
|---|
| 11358 | @@ -174,6 +174,16 @@ public class BBox implements IBounds {
|
|---|
| 11359 | * @param extraSpace the value to extend the primitives bbox. Unit is in LatLon degrees.
|
|---|
| 11360 | */
|
|---|
| 11361 | public void addPrimitive(OsmPrimitive primitive, double extraSpace) {
|
|---|
| 11362 | + this.addPrimitive((IPrimitive) primitive, extraSpace);
|
|---|
| 11363 | + }
|
|---|
| 11364 | +
|
|---|
| 11365 | + /**
|
|---|
| 11366 | + * Extends this bbox to include the bbox of the primitive extended by extraSpace.
|
|---|
| 11367 | + * @param primitive an primitive
|
|---|
| 11368 | + * @param extraSpace the value to extend the primitives bbox. Unit is in LatLon degrees.
|
|---|
| 11369 | + * @since xxx
|
|---|
| 11370 | + */
|
|---|
| 11371 | + public void addPrimitive(IPrimitive primitive, double extraSpace) {
|
|---|
| 11372 | IBounds primBbox = primitive.getBBox();
|
|---|
| 11373 | add(primBbox.getMinLon() - extraSpace, primBbox.getMinLat() - extraSpace);
|
|---|
| 11374 | add(primBbox.getMaxLon() + extraSpace, primBbox.getMaxLat() + extraSpace);
|
|---|
| 11375 | --
|
|---|
| 11376 | GitLab
|
|---|
| 11377 |
|
|---|
| 11378 |
|
|---|
| 11379 | From f898d4e21e97655892cea722693c27f0a8408931 Mon Sep 17 00:00:00 2001
|
|---|
| 11380 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 11381 | Date: Wed, 28 Apr 2021 12:18:03 -0600
|
|---|
| 11382 | Subject: [PATCH 22/50] Vector Data: Performance fix, fix an
|
|---|
| 11383 | UnsupportedOperationException
|
|---|
| 11384 |
|
|---|
| 11385 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 11386 | ---
|
|---|
| 11387 | .../openstreetmap/josm/data/vector/DataStore.java | 12 +++++++-----
|
|---|
| 11388 | .../josm/data/vector/VectorDataSet.java | 13 +++++++++----
|
|---|
| 11389 | 2 files changed, 16 insertions(+), 9 deletions(-)
|
|---|
| 11390 |
|
|---|
| 11391 | diff --git a/src/org/openstreetmap/josm/data/vector/DataStore.java b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 11392 | index 86f1948d1..d3fb4ac59 100644
|
|---|
| 11393 | --- a/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 11394 | +++ b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 11395 | @@ -49,8 +49,8 @@ class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R exte
|
|---|
| 11396 | protected final Storage<O> allPrimitives = new Storage<>(new Storage.PrimitiveIdHash(), true);
|
|---|
| 11397 | // TODO what happens when I use hashCode?
|
|---|
| 11398 | protected final Set<Tile> addedTiles = Collections.synchronizedSet(new HashSet<>());
|
|---|
| 11399 | - protected final Map<PrimitiveId, O> primitivesMap = allPrimitives
|
|---|
| 11400 | - .foreignKey(new Storage.PrimitiveIdHash());
|
|---|
| 11401 | + protected final Map<PrimitiveId, O> primitivesMap = Collections.synchronizedMap(allPrimitives
|
|---|
| 11402 | + .foreignKey(new Storage.PrimitiveIdHash()));
|
|---|
| 11403 | protected final Collection<DataSource> dataSources = new LinkedList<>();
|
|---|
| 11404 | private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
|---|
| 11405 |
|
|---|
| 11406 | @@ -70,10 +70,12 @@ class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R exte
|
|---|
| 11407 | return this.allPrimitives;
|
|---|
| 11408 | }
|
|---|
| 11409 |
|
|---|
| 11410 | + /**
|
|---|
| 11411 | + * Get the primitives map.
|
|---|
| 11412 | + * @implNote The returned map is a {@link Collections#synchronizedMap}. Please synchronize on it.
|
|---|
| 11413 | + * @return The Primitives map.
|
|---|
| 11414 | + */
|
|---|
| 11415 | public Map<PrimitiveId, O> getPrimitivesMap() {
|
|---|
| 11416 | - if (this.readWriteLock.isWriteLocked()) {
|
|---|
| 11417 | - return new HashMap<>(this.primitivesMap);
|
|---|
| 11418 | - }
|
|---|
| 11419 | return this.primitivesMap;
|
|---|
| 11420 | }
|
|---|
| 11421 |
|
|---|
| 11422 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11423 | index 229fdf40e..b262194ab 100644
|
|---|
| 11424 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11425 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11426 | @@ -34,6 +34,7 @@ import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 11427 | import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 11428 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
|---|
| 11429 | import org.openstreetmap.josm.data.osm.PrimitiveId;
|
|---|
| 11430 | +import org.openstreetmap.josm.data.osm.Storage;
|
|---|
| 11431 | import org.openstreetmap.josm.data.osm.UploadPolicy;
|
|---|
| 11432 | import org.openstreetmap.josm.data.osm.WaySegment;
|
|---|
| 11433 | import org.openstreetmap.josm.data.osm.event.IDataSelectionEventSource;
|
|---|
| 11434 | @@ -314,10 +315,14 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11435 |
|
|---|
| 11436 | @Override
|
|---|
| 11437 | public Collection<VectorPrimitive> getAllSelected() {
|
|---|
| 11438 | - final Optional<VectorDataStore> dataStore = this.getBestZoomDataStore();
|
|---|
| 11439 | - return dataStore.map(vectorDataStore -> vectorDataStore.getAllPrimitives().stream()
|
|---|
| 11440 | - .filter(primitive -> this.currentSelectedPrimitives.contains(primitive.getPrimitiveId()))
|
|---|
| 11441 | - .collect(Collectors.toList())).orElse(Collections.emptyList());
|
|---|
| 11442 | + final Map<PrimitiveId, VectorPrimitive> dataStore = this.getBestZoomDataStore().map(VectorDataStore::getPrimitivesMap).orElse(null);
|
|---|
| 11443 | + if (dataStore != null) {
|
|---|
| 11444 | + // The dataStore is a final variable from the VectorDataStore.
|
|---|
| 11445 | + synchronized (dataStore) {
|
|---|
| 11446 | + return this.currentSelectedPrimitives.stream().map(dataStore::get).collect(Collectors.toList());
|
|---|
| 11447 | + }
|
|---|
| 11448 | + }
|
|---|
| 11449 | + return Collections.emptyList();
|
|---|
| 11450 | }
|
|---|
| 11451 |
|
|---|
| 11452 | /**
|
|---|
| 11453 | --
|
|---|
| 11454 | GitLab
|
|---|
| 11455 |
|
|---|
| 11456 |
|
|---|
| 11457 | From 3d0eb727137005aa60ff0bd48dd1acfc38453d57 Mon Sep 17 00:00:00 2001
|
|---|
| 11458 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 11459 | Date: Wed, 28 Apr 2021 13:08:57 -0600
|
|---|
| 11460 | Subject: [PATCH 23/50] VectorData: Add method to highlight primitives
|
|---|
| 11461 |
|
|---|
| 11462 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 11463 | ---
|
|---|
| 11464 | .../josm/data/vector/VectorDataSet.java | 23 ++++++++++++++++++-
|
|---|
| 11465 | 1 file changed, 22 insertions(+), 1 deletion(-)
|
|---|
| 11466 |
|
|---|
| 11467 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11468 | index b262194ab..7e82030eb 100644
|
|---|
| 11469 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11470 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11471 | @@ -101,6 +101,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11472 | * The paint style for this layer
|
|---|
| 11473 | */
|
|---|
| 11474 | private ElemStyles styles;
|
|---|
| 11475 | + private final Collection<PrimitiveId> highlighted = new HashSet<>();
|
|---|
| 11476 |
|
|---|
| 11477 | @Override
|
|---|
| 11478 | public Collection<DataSource> getDataSources() {
|
|---|
| 11479 | @@ -303,6 +304,26 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11480 | // TODO? This requires a change to WaySegment so that it isn't Way/Node specific
|
|---|
| 11481 | }
|
|---|
| 11482 |
|
|---|
| 11483 | + /**
|
|---|
| 11484 | + * Mark some primitives as highlighted
|
|---|
| 11485 | + * @param primitives The primitives to highlight
|
|---|
| 11486 | + * @apiNote This is *highly likely* to change, as the inherited methods are modified to accept primitives other than OSM primitives.
|
|---|
| 11487 | + */
|
|---|
| 11488 | + public void setHighlighted(Collection<PrimitiveId> primitives) {
|
|---|
| 11489 | + this.highlighted.clear();
|
|---|
| 11490 | + this.highlighted.addAll(primitives);
|
|---|
| 11491 | + // The highlight event updates are very OSM specific, and require a DataSet.
|
|---|
| 11492 | + this.highlightUpdateListenerListenerList.fireEvent(event -> event.highlightUpdated(null));
|
|---|
| 11493 | + }
|
|---|
| 11494 | +
|
|---|
| 11495 | + /**
|
|---|
| 11496 | + * Get the highlighted objects
|
|---|
| 11497 | + * @return The highlighted objects
|
|---|
| 11498 | + */
|
|---|
| 11499 | + public Collection<PrimitiveId> getHighlighted() {
|
|---|
| 11500 | + return Collections.unmodifiableCollection(this.highlighted);
|
|---|
| 11501 | + }
|
|---|
| 11502 | +
|
|---|
| 11503 | @Override
|
|---|
| 11504 | public void addHighlightUpdateListener(HighlightUpdateListener listener) {
|
|---|
| 11505 | this.highlightUpdateListenerListenerList.addListener(listener);
|
|---|
| 11506 | @@ -319,7 +340,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11507 | if (dataStore != null) {
|
|---|
| 11508 | // The dataStore is a final variable from the VectorDataStore.
|
|---|
| 11509 | synchronized (dataStore) {
|
|---|
| 11510 | - return this.currentSelectedPrimitives.stream().map(dataStore::get).collect(Collectors.toList());
|
|---|
| 11511 | + return this.currentSelectedPrimitives.stream().map(dataStore::get).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 11512 | }
|
|---|
| 11513 | }
|
|---|
| 11514 | return Collections.emptyList();
|
|---|
| 11515 | --
|
|---|
| 11516 | GitLab
|
|---|
| 11517 |
|
|---|
| 11518 |
|
|---|
| 11519 | From fccaf1e53603d221f18667995da72cb6f37ea12f Mon Sep 17 00:00:00 2001
|
|---|
| 11520 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 11521 | Date: Thu, 29 Apr 2021 11:32:34 -0600
|
|---|
| 11522 | Subject: [PATCH 24/50] VectorTiles: Rework to avoid locks preventing paint
|
|---|
| 11523 |
|
|---|
| 11524 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 11525 | ---
|
|---|
| 11526 | .../data/imagery/vectortile/mapbox/Layer.java | 10 +-
|
|---|
| 11527 | .../imagery/vectortile/mapbox/MVTTile.java | 70 +++--
|
|---|
| 11528 | src/org/openstreetmap/josm/data/osm/BBox.java | 5 +-
|
|---|
| 11529 | .../osm/event/IDataSelectionEventSource.java | 5 +-
|
|---|
| 11530 | .../osm/event/IDataSelectionListener.java | 35 ++-
|
|---|
| 11531 | .../josm/data/vector/DataStore.java | 10 -
|
|---|
| 11532 | .../josm/data/vector/VectorDataSet.java | 249 ++++++++++--------
|
|---|
| 11533 | .../josm/data/vector/VectorDataStore.java | 82 ++----
|
|---|
| 11534 | 8 files changed, 240 insertions(+), 226 deletions(-)
|
|---|
| 11535 |
|
|---|
| 11536 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 11537 | index 1c496d55d..0a6bb073e 100644
|
|---|
| 11538 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 11539 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 11540 | @@ -16,6 +16,7 @@ import java.util.stream.Collectors;
|
|---|
| 11541 |
|
|---|
| 11542 | import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 11543 | import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 11544 | +import org.openstreetmap.josm.tools.Destroyable;
|
|---|
| 11545 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 11546 |
|
|---|
| 11547 | /**
|
|---|
| 11548 | @@ -23,7 +24,7 @@ import org.openstreetmap.josm.tools.Logging;
|
|---|
| 11549 | * @author Taylor Smock
|
|---|
| 11550 | * @since xxx
|
|---|
| 11551 | */
|
|---|
| 11552 | -public final class Layer {
|
|---|
| 11553 | +public final class Layer implements Destroyable {
|
|---|
| 11554 | private static final class ValueFields<T> {
|
|---|
| 11555 | static final ValueFields<String> STRING = new ValueFields<>(1, ProtoBufRecord::asString);
|
|---|
| 11556 | static final ValueFields<Float> FLOAT = new ValueFields<>(2, ProtoBufRecord::asFloat);
|
|---|
| 11557 | @@ -224,6 +225,13 @@ public final class Layer {
|
|---|
| 11558 | return this.version;
|
|---|
| 11559 | }
|
|---|
| 11560 |
|
|---|
| 11561 | + @Override
|
|---|
| 11562 | + public void destroy() {
|
|---|
| 11563 | + this.featureCollection.clear();
|
|---|
| 11564 | + this.keyList.clear();
|
|---|
| 11565 | + this.valueList.clear();
|
|---|
| 11566 | + }
|
|---|
| 11567 | +
|
|---|
| 11568 | @Override
|
|---|
| 11569 | public boolean equals(Object other) {
|
|---|
| 11570 | if (other instanceof Layer) {
|
|---|
| 11571 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 11572 | index 5d1d781dd..ab77c43f4 100644
|
|---|
| 11573 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 11574 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 11575 | @@ -1,33 +1,39 @@
|
|---|
| 11576 | // License: GPL. For details, see LICENSE file.
|
|---|
| 11577 | package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
|
|---|
| 11578 |
|
|---|
| 11579 | -import java.awt.image.BufferedImage;
|
|---|
| 11580 | -import java.io.IOException;
|
|---|
| 11581 | -import java.io.InputStream;
|
|---|
| 11582 | -import java.util.Collection;
|
|---|
| 11583 | -import java.util.HashSet;
|
|---|
| 11584 | -import java.util.List;
|
|---|
| 11585 | -import java.util.stream.Collectors;
|
|---|
| 11586 | -
|
|---|
| 11587 | import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 11588 | +import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
|---|
| 11589 | import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
|
|---|
| 11590 | +import org.openstreetmap.josm.data.IQuadBucketType;
|
|---|
| 11591 | import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 11592 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 11593 | import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 11594 | import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 11595 | +import org.openstreetmap.josm.data.vector.VectorDataStore;
|
|---|
| 11596 | import org.openstreetmap.josm.tools.ListenerList;
|
|---|
| 11597 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 11598 |
|
|---|
| 11599 | +import java.awt.image.BufferedImage;
|
|---|
| 11600 | +import java.io.IOException;
|
|---|
| 11601 | +import java.io.InputStream;
|
|---|
| 11602 | +import java.util.Collection;
|
|---|
| 11603 | +import java.util.HashSet;
|
|---|
| 11604 | +import java.util.List;
|
|---|
| 11605 | +import java.util.stream.Collectors;
|
|---|
| 11606 | +
|
|---|
| 11607 | /**
|
|---|
| 11608 | * A class for MapBox Vector Tiles
|
|---|
| 11609 | *
|
|---|
| 11610 | * @author Taylor Smock
|
|---|
| 11611 | * @since xxx
|
|---|
| 11612 | */
|
|---|
| 11613 | -public class MVTTile extends Tile implements VectorTile {
|
|---|
| 11614 | +public class MVTTile extends Tile implements VectorTile, IQuadBucketType {
|
|---|
| 11615 | private final ListenerList<TileListener> listenerList = ListenerList.create();
|
|---|
| 11616 | private Collection<Layer> layers;
|
|---|
| 11617 | private int extent = Layer.DEFAULT_EXTENT;
|
|---|
| 11618 | static final BufferedImage CLEAR_LOADED = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
|
|---|
| 11619 | + private BBox bbox;
|
|---|
| 11620 | + private VectorDataStore vectorDataStore;
|
|---|
| 11621 |
|
|---|
| 11622 | /**
|
|---|
| 11623 | * Create a new Tile
|
|---|
| 11624 | @@ -47,25 +53,30 @@ public class MVTTile extends Tile implements VectorTile {
|
|---|
| 11625 | ProtoBufParser parser = new ProtoBufParser(inputStream);
|
|---|
| 11626 | Collection<ProtoBufRecord> protoBufRecords = parser.allRecords();
|
|---|
| 11627 | this.layers = new HashSet<>();
|
|---|
| 11628 | - this.layers = protoBufRecords.stream().map(record -> {
|
|---|
| 11629 | + this.layers = protoBufRecords.stream().map(protoBufRecord -> {
|
|---|
| 11630 | Layer mvtLayer = null;
|
|---|
| 11631 | - if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 11632 | - try (ProtoBufParser tParser = new ProtoBufParser(record.getBytes())) {
|
|---|
| 11633 | + if (protoBufRecord.getField() == Layer.LAYER_FIELD) {
|
|---|
| 11634 | + try (ProtoBufParser tParser = new ProtoBufParser(protoBufRecord.getBytes())) {
|
|---|
| 11635 | mvtLayer = new Layer(tParser.allRecords());
|
|---|
| 11636 | } catch (IOException e) {
|
|---|
| 11637 | Logging.error(e);
|
|---|
| 11638 | } finally {
|
|---|
| 11639 | // Cleanup bytes
|
|---|
| 11640 | - record.close();
|
|---|
| 11641 | + protoBufRecord.close();
|
|---|
| 11642 | }
|
|---|
| 11643 | }
|
|---|
| 11644 | return mvtLayer;
|
|---|
| 11645 | }).collect(Collectors.toCollection(HashSet::new));
|
|---|
| 11646 | this.extent = layers.stream().map(Layer::getExtent).max(Integer::compare).orElse(Layer.DEFAULT_EXTENT);
|
|---|
| 11647 | - this.finishLoading();
|
|---|
| 11648 | - this.listenerList.fireEvent(event -> event.finishedLoading(this));
|
|---|
| 11649 | - // Ensure that we don't keep the loading image around
|
|---|
| 11650 | - this.image = CLEAR_LOADED;
|
|---|
| 11651 | + if (this.getData() != null) {
|
|---|
| 11652 | + this.finishLoading();
|
|---|
| 11653 | + this.listenerList.fireEvent(event -> event.finishedLoading(this));
|
|---|
| 11654 | + // Ensure that we don't keep the loading image around
|
|---|
| 11655 | + this.image = CLEAR_LOADED;
|
|---|
| 11656 | + // Cleanup as much as possible -- layers will still exist, but only base information (like name, extent) will remain.
|
|---|
| 11657 | + // Called last just in case the listeners need the layers.
|
|---|
| 11658 | + this.layers.forEach(Layer::destroy);
|
|---|
| 11659 | + }
|
|---|
| 11660 | }
|
|---|
| 11661 | }
|
|---|
| 11662 |
|
|---|
| 11663 | @@ -89,6 +100,31 @@ public class MVTTile extends Tile implements VectorTile {
|
|---|
| 11664 | this.listenerList.addWeakListener(listener);
|
|---|
| 11665 | }
|
|---|
| 11666 |
|
|---|
| 11667 | + @Override
|
|---|
| 11668 | + public BBox getBBox() {
|
|---|
| 11669 | + if (this.bbox == null) {
|
|---|
| 11670 | + final ICoordinate upperLeft = this.getTileSource().tileXYToLatLon(this);
|
|---|
| 11671 | + final ICoordinate lowerRight = this.getTileSource()
|
|---|
| 11672 | + .tileXYToLatLon(this.getXtile() + 1, this.getYtile() + 1, this.getZoom());
|
|---|
| 11673 | + BBox newBBox = new BBox(upperLeft.getLon(), upperLeft.getLat(), lowerRight.getLon(), lowerRight.getLat());
|
|---|
| 11674 | + this.bbox = newBBox.toImmutable();
|
|---|
| 11675 | + }
|
|---|
| 11676 | + return this.bbox;
|
|---|
| 11677 | + }
|
|---|
| 11678 | +
|
|---|
| 11679 | + /**
|
|---|
| 11680 | + * Get the datastore for this tile
|
|---|
| 11681 | + * @return The data
|
|---|
| 11682 | + */
|
|---|
| 11683 | + public VectorDataStore getData() {
|
|---|
| 11684 | + if (this.vectorDataStore == null) {
|
|---|
| 11685 | + VectorDataStore newDataStore = new VectorDataStore();
|
|---|
| 11686 | + newDataStore.addDataTile(this);
|
|---|
| 11687 | + this.vectorDataStore = newDataStore;
|
|---|
| 11688 | + }
|
|---|
| 11689 | + return this.vectorDataStore;
|
|---|
| 11690 | + }
|
|---|
| 11691 | +
|
|---|
| 11692 | /**
|
|---|
| 11693 | * A class that can be notified that a tile has finished loading
|
|---|
| 11694 | *
|
|---|
| 11695 | diff --git a/src/org/openstreetmap/josm/data/osm/BBox.java b/src/org/openstreetmap/josm/data/osm/BBox.java
|
|---|
| 11696 | index 7dcc79a41..106037ec9 100644
|
|---|
| 11697 | --- a/src/org/openstreetmap/josm/data/osm/BBox.java
|
|---|
| 11698 | +++ b/src/org/openstreetmap/josm/data/osm/BBox.java
|
|---|
| 11699 | @@ -465,8 +465,9 @@ public class BBox implements IBounds {
|
|---|
| 11700 | /**
|
|---|
| 11701 | * Returns an immutable version of this bbox, i.e., modifying calls throw an {@link UnsupportedOperationException}.
|
|---|
| 11702 | * @return an immutable version of this bbox
|
|---|
| 11703 | + * @since xxx (interface)
|
|---|
| 11704 | */
|
|---|
| 11705 | - BBox toImmutable() {
|
|---|
| 11706 | + public BBox toImmutable() {
|
|---|
| 11707 | return new Immutable(this);
|
|---|
| 11708 | }
|
|---|
| 11709 |
|
|---|
| 11710 | @@ -482,7 +483,7 @@ public class BBox implements IBounds {
|
|---|
| 11711 | }
|
|---|
| 11712 |
|
|---|
| 11713 | @Override
|
|---|
| 11714 | - BBox toImmutable() {
|
|---|
| 11715 | + public BBox toImmutable() {
|
|---|
| 11716 | return this;
|
|---|
| 11717 | }
|
|---|
| 11718 | }
|
|---|
| 11719 | diff --git a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 11720 | index e5fc0ea19..4f1d75d18 100644
|
|---|
| 11721 | --- a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 11722 | +++ b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 11723 | @@ -10,14 +10,15 @@ import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 11724 | /**
|
|---|
| 11725 | * This interface indicates that the class can fire {@link IDataSelectionListener}.
|
|---|
| 11726 | * @author Taylor Smock, Michael Zangl (original code)
|
|---|
| 11727 | - * @since xxx
|
|---|
| 11728 | * @param <O> the base type of OSM primitives
|
|---|
| 11729 | * @param <N> type representing OSM nodes
|
|---|
| 11730 | * @param <W> type representing OSM ways
|
|---|
| 11731 | * @param <R> type representing OSM relations
|
|---|
| 11732 | * @param <D> The dataset type
|
|---|
| 11733 | + * @since xxx
|
|---|
| 11734 | */
|
|---|
| 11735 | -public interface IDataSelectionEventSource<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> {
|
|---|
| 11736 | +public interface IDataSelectionEventSource<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>,
|
|---|
| 11737 | + D extends OsmData<O, N, W, R>> {
|
|---|
| 11738 | /**
|
|---|
| 11739 | * Add a listener
|
|---|
| 11740 | * @param listener The listener to add
|
|---|
| 11741 | diff --git a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java
|
|---|
| 11742 | index 5a95aa1c9..7550e8dbd 100644
|
|---|
| 11743 | --- a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java
|
|---|
| 11744 | +++ b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionListener.java
|
|---|
| 11745 | @@ -20,15 +20,16 @@ import java.util.stream.Stream;
|
|---|
| 11746 | /**
|
|---|
| 11747 | * This interface is the same as {@link DataSelectionListener}, except it isn't {@link OsmPrimitive} specific.
|
|---|
| 11748 | * @author Taylor Smock, Michael Zangl (original code)
|
|---|
| 11749 | - * @since xxx
|
|---|
| 11750 | * @param <O> the base type of OSM primitives
|
|---|
| 11751 | * @param <N> type representing OSM nodes
|
|---|
| 11752 | * @param <W> type representing OSM ways
|
|---|
| 11753 | * @param <R> type representing OSM relations
|
|---|
| 11754 | * @param <D> The dataset type
|
|---|
| 11755 | + * @since xxx
|
|---|
| 11756 | */
|
|---|
| 11757 | @FunctionalInterface
|
|---|
| 11758 | -public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> {
|
|---|
| 11759 | +public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>,
|
|---|
| 11760 | + D extends OsmData<O, N, W, R>> {
|
|---|
| 11761 | /**
|
|---|
| 11762 | * Called whenever the selection is changed.
|
|---|
| 11763 | *
|
|---|
| 11764 | @@ -41,14 +42,15 @@ public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W
|
|---|
| 11765 | /**
|
|---|
| 11766 | * The event that is fired when the selection changed.
|
|---|
| 11767 | * @author Michael Zangl
|
|---|
| 11768 | - * @since xxx generics
|
|---|
| 11769 | * @param <O> the base type of OSM primitives
|
|---|
| 11770 | * @param <N> type representing OSM nodes
|
|---|
| 11771 | * @param <W> type representing OSM ways
|
|---|
| 11772 | * @param <R> type representing OSM relations
|
|---|
| 11773 | * @param <D> The dataset type
|
|---|
| 11774 | + * @since xxx generics
|
|---|
| 11775 | */
|
|---|
| 11776 | - interface SelectionChangeEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> {
|
|---|
| 11777 | + interface SelectionChangeEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>,
|
|---|
| 11778 | + D extends OsmData<O, N, W, R>> {
|
|---|
| 11779 | /**
|
|---|
| 11780 | * Gets the previous selection
|
|---|
| 11781 | * <p>
|
|---|
| 11782 | @@ -105,14 +107,15 @@ public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W
|
|---|
| 11783 | /**
|
|---|
| 11784 | * The base class for selection events
|
|---|
| 11785 | * @author Michael Zangl
|
|---|
| 11786 | - * @since 12048, xxx (generics)
|
|---|
| 11787 | * @param <O> the base type of OSM primitives
|
|---|
| 11788 | * @param <N> type representing OSM nodes
|
|---|
| 11789 | * @param <W> type representing OSM ways
|
|---|
| 11790 | * @param <R> type representing OSM relations
|
|---|
| 11791 | * @param <D> The dataset type
|
|---|
| 11792 | + * @since 12048, xxx (generics)
|
|---|
| 11793 | */
|
|---|
| 11794 | - abstract class AbstractSelectionEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> implements SelectionChangeEvent<O, N, W, R, D> {
|
|---|
| 11795 | + abstract class AbstractSelectionEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>,
|
|---|
| 11796 | + D extends OsmData<O, N, W, R>> implements SelectionChangeEvent<O, N, W, R, D> {
|
|---|
| 11797 | private final D source;
|
|---|
| 11798 | private final Set<O> old;
|
|---|
| 11799 |
|
|---|
| 11800 | @@ -137,14 +140,15 @@ public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W
|
|---|
| 11801 | /**
|
|---|
| 11802 | * The selection is replaced by a new selection
|
|---|
| 11803 | * @author Michael Zangl
|
|---|
| 11804 | - * @since xxx (generics)
|
|---|
| 11805 | * @param <O> the base type of OSM primitives
|
|---|
| 11806 | * @param <N> type representing OSM nodes
|
|---|
| 11807 | * @param <W> type representing OSM ways
|
|---|
| 11808 | * @param <R> type representing OSM relations
|
|---|
| 11809 | * @param <D> The dataset type
|
|---|
| 11810 | + * @since xxx (generics)
|
|---|
| 11811 | */
|
|---|
| 11812 | - class SelectionReplaceEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11813 | + class SelectionReplaceEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>>
|
|---|
| 11814 | + extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11815 | private final Set<O> current;
|
|---|
| 11816 | private Set<O> removed;
|
|---|
| 11817 | private Set<O> added;
|
|---|
| 11818 | @@ -193,14 +197,15 @@ public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W
|
|---|
| 11819 | /**
|
|---|
| 11820 | * Primitives are added to the selection
|
|---|
| 11821 | * @author Michael Zangl
|
|---|
| 11822 | - * @since xxx (generics)
|
|---|
| 11823 | * @param <O> the base type of OSM primitives
|
|---|
| 11824 | * @param <N> type representing OSM nodes
|
|---|
| 11825 | * @param <W> type representing OSM ways
|
|---|
| 11826 | * @param <R> type representing OSM relations
|
|---|
| 11827 | * @param <D> The dataset type
|
|---|
| 11828 | + * @since xxx (generics)
|
|---|
| 11829 | */
|
|---|
| 11830 | - class SelectionAddEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11831 | + class SelectionAddEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>>
|
|---|
| 11832 | + extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11833 | private final Set<O> add;
|
|---|
| 11834 | private final Set<O> current;
|
|---|
| 11835 |
|
|---|
| 11836 | @@ -247,14 +252,15 @@ public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W
|
|---|
| 11837 | /**
|
|---|
| 11838 | * Primitives are removed from the selection
|
|---|
| 11839 | * @author Michael Zangl
|
|---|
| 11840 | - * @since 12048, xxx (generics)
|
|---|
| 11841 | * @param <O> the base type of OSM primitives
|
|---|
| 11842 | * @param <N> type representing OSM nodes
|
|---|
| 11843 | * @param <W> type representing OSM ways
|
|---|
| 11844 | * @param <R> type representing OSM relations
|
|---|
| 11845 | * @param <D> The dataset type
|
|---|
| 11846 | + * @since 12048, xxx (generics)
|
|---|
| 11847 | */
|
|---|
| 11848 | - class SelectionRemoveEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11849 | + class SelectionRemoveEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>>
|
|---|
| 11850 | + extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11851 | private final Set<O> remove;
|
|---|
| 11852 | private final Set<O> current;
|
|---|
| 11853 |
|
|---|
| 11854 | @@ -302,14 +308,15 @@ public interface IDataSelectionListener<O extends IPrimitive, N extends INode, W
|
|---|
| 11855 | /**
|
|---|
| 11856 | * Toggle the selected state of a primitive
|
|---|
| 11857 | * @author Michael Zangl
|
|---|
| 11858 | - * @since xxx (generics)
|
|---|
| 11859 | * @param <O> the base type of OSM primitives
|
|---|
| 11860 | * @param <N> type representing OSM nodes
|
|---|
| 11861 | * @param <W> type representing OSM ways
|
|---|
| 11862 | * @param <R> type representing OSM relations
|
|---|
| 11863 | * @param <D> The dataset type
|
|---|
| 11864 | + * @since xxx (generics)
|
|---|
| 11865 | */
|
|---|
| 11866 | - class SelectionToggleEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>> extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11867 | + class SelectionToggleEvent<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>, D extends OsmData<O, N, W, R>>
|
|---|
| 11868 | + extends AbstractSelectionEvent<O, N, W, R, D> {
|
|---|
| 11869 | private final Set<O> current;
|
|---|
| 11870 | private final Set<O> remove;
|
|---|
| 11871 | private final Set<O> add;
|
|---|
| 11872 | diff --git a/src/org/openstreetmap/josm/data/vector/DataStore.java b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 11873 | index d3fb4ac59..9f942a0d6 100644
|
|---|
| 11874 | --- a/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 11875 | +++ b/src/org/openstreetmap/josm/data/vector/DataStore.java
|
|---|
| 11876 | @@ -3,7 +3,6 @@ package org.openstreetmap.josm.data.vector;
|
|---|
| 11877 |
|
|---|
| 11878 | import java.util.Collection;
|
|---|
| 11879 | import java.util.Collections;
|
|---|
| 11880 | -import java.util.HashMap;
|
|---|
| 11881 | import java.util.HashSet;
|
|---|
| 11882 | import java.util.LinkedList;
|
|---|
| 11883 | import java.util.Map;
|
|---|
| 11884 | @@ -44,7 +43,6 @@ class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R exte
|
|---|
| 11885 | }
|
|---|
| 11886 | }
|
|---|
| 11887 |
|
|---|
| 11888 | - protected final int zoom;
|
|---|
| 11889 | protected final LocalQuadBucketPrimitiveStore<N, W, R> store = new LocalQuadBucketPrimitiveStore<>();
|
|---|
| 11890 | protected final Storage<O> allPrimitives = new Storage<>(new Storage.PrimitiveIdHash(), true);
|
|---|
| 11891 | // TODO what happens when I use hashCode?
|
|---|
| 11892 | @@ -54,14 +52,6 @@ class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R exte
|
|---|
| 11893 | protected final Collection<DataSource> dataSources = new LinkedList<>();
|
|---|
| 11894 | private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
|---|
| 11895 |
|
|---|
| 11896 | - DataStore(int zoom) {
|
|---|
| 11897 | - this.zoom = zoom;
|
|---|
| 11898 | - }
|
|---|
| 11899 | -
|
|---|
| 11900 | - public int getZoom() {
|
|---|
| 11901 | - return this.zoom;
|
|---|
| 11902 | - }
|
|---|
| 11903 | -
|
|---|
| 11904 | public QuadBucketPrimitiveStore<N, W, R> getStore() {
|
|---|
| 11905 | return this.store;
|
|---|
| 11906 | }
|
|---|
| 11907 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11908 | index 7e82030eb..55366d555 100644
|
|---|
| 11909 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11910 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 11911 | @@ -1,6 +1,25 @@
|
|---|
| 11912 | // License: GPL. For details, see LICENSE file.
|
|---|
| 11913 | package org.openstreetmap.josm.data.vector;
|
|---|
| 11914 |
|
|---|
| 11915 | +import org.openstreetmap.josm.data.DataSource;
|
|---|
| 11916 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 11917 | +import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 11918 | +import org.openstreetmap.josm.data.osm.DataSelectionListener;
|
|---|
| 11919 | +import org.openstreetmap.josm.data.osm.DownloadPolicy;
|
|---|
| 11920 | +import org.openstreetmap.josm.data.osm.HighlightUpdateListener;
|
|---|
| 11921 | +import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 11922 | +import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 11923 | +import org.openstreetmap.josm.data.osm.PrimitiveId;
|
|---|
| 11924 | +import org.openstreetmap.josm.data.osm.Storage;
|
|---|
| 11925 | +import org.openstreetmap.josm.data.osm.UploadPolicy;
|
|---|
| 11926 | +import org.openstreetmap.josm.data.osm.WaySegment;
|
|---|
| 11927 | +import org.openstreetmap.josm.data.osm.event.IDataSelectionEventSource;
|
|---|
| 11928 | +import org.openstreetmap.josm.data.osm.event.IDataSelectionListener;
|
|---|
| 11929 | +import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 11930 | +import org.openstreetmap.josm.tools.ListenerList;
|
|---|
| 11931 | +import org.openstreetmap.josm.tools.Logging;
|
|---|
| 11932 | +import org.openstreetmap.josm.tools.SubclassFilteredCollection;
|
|---|
| 11933 | +
|
|---|
| 11934 | import java.util.ArrayList;
|
|---|
| 11935 | import java.util.Arrays;
|
|---|
| 11936 | import java.util.Collection;
|
|---|
| 11937 | @@ -19,46 +38,22 @@ import java.util.function.Function;
|
|---|
| 11938 | import java.util.function.Predicate;
|
|---|
| 11939 | import java.util.function.Supplier;
|
|---|
| 11940 | import java.util.stream.Collectors;
|
|---|
| 11941 | -import java.util.stream.IntStream;
|
|---|
| 11942 | import java.util.stream.Stream;
|
|---|
| 11943 |
|
|---|
| 11944 | -import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 11945 | -import org.openstreetmap.josm.data.DataSource;
|
|---|
| 11946 | -import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 11947 | -import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 11948 | -import org.openstreetmap.josm.data.osm.DataSelectionListener;
|
|---|
| 11949 | -import org.openstreetmap.josm.data.osm.DataSet;
|
|---|
| 11950 | -import org.openstreetmap.josm.data.osm.DownloadPolicy;
|
|---|
| 11951 | -import org.openstreetmap.josm.data.osm.HighlightUpdateListener;
|
|---|
| 11952 | -import org.openstreetmap.josm.data.osm.IPrimitive;
|
|---|
| 11953 | -import org.openstreetmap.josm.data.osm.OsmData;
|
|---|
| 11954 | -import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
|---|
| 11955 | -import org.openstreetmap.josm.data.osm.PrimitiveId;
|
|---|
| 11956 | -import org.openstreetmap.josm.data.osm.Storage;
|
|---|
| 11957 | -import org.openstreetmap.josm.data.osm.UploadPolicy;
|
|---|
| 11958 | -import org.openstreetmap.josm.data.osm.WaySegment;
|
|---|
| 11959 | -import org.openstreetmap.josm.data.osm.event.IDataSelectionEventSource;
|
|---|
| 11960 | -import org.openstreetmap.josm.data.osm.event.IDataSelectionListener;
|
|---|
| 11961 | -import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 11962 | -import org.openstreetmap.josm.tools.ListenerList;
|
|---|
| 11963 | -import org.openstreetmap.josm.tools.Logging;
|
|---|
| 11964 | -import org.openstreetmap.josm.tools.SubclassFilteredCollection;
|
|---|
| 11965 | -
|
|---|
| 11966 | /**
|
|---|
| 11967 | * A data class for Vector Data
|
|---|
| 11968 | *
|
|---|
| 11969 | * @author Taylor Smock
|
|---|
| 11970 | * @since xxx
|
|---|
| 11971 | */
|
|---|
| 11972 | -public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, VectorWay, VectorRelation>, IDataSelectionEventSource<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> {
|
|---|
| 11973 | +public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, VectorWay, VectorRelation>,
|
|---|
| 11974 | + IDataSelectionEventSource<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> {
|
|---|
| 11975 | // Note: In Java 8, computeIfAbsent is blocking for both pre-existing and new values. In Java 9, it is only blocking
|
|---|
| 11976 | // for new values (perf increase). See JDK-8161372 for more info.
|
|---|
| 11977 | - private final Map<Integer, VectorDataStore> dataStoreMap = new ConcurrentHashMap<>();
|
|---|
| 11978 | + private final Map<Integer, Storage<MVTTile>> dataStoreMap = new ConcurrentHashMap<>();
|
|---|
| 11979 | // Both of these listener lists are useless, since they expect OsmPrimitives at this time
|
|---|
| 11980 | private final ListenerList<HighlightUpdateListener> highlightUpdateListenerListenerList = ListenerList.create();
|
|---|
| 11981 | private final ListenerList<DataSelectionListener> dataSelectionListenerListenerList = ListenerList.create();
|
|---|
| 11982 | - private static final String[] NO_INVISIBLE_LAYERS = new String[0];
|
|---|
| 11983 | - private String[] invisibleLayers = NO_INVISIBLE_LAYERS;
|
|---|
| 11984 | private boolean lock = true;
|
|---|
| 11985 | private String name;
|
|---|
| 11986 | private short mappaintCacheIdx = 1;
|
|---|
| 11987 | @@ -105,20 +100,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 11988 |
|
|---|
| 11989 | @Override
|
|---|
| 11990 | public Collection<DataSource> getDataSources() {
|
|---|
| 11991 | - final int currentZoom = this.zoom;
|
|---|
| 11992 | - final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 11993 | - return dataStore.getDataSources();
|
|---|
| 11994 | - }
|
|---|
| 11995 | -
|
|---|
| 11996 | - /**
|
|---|
| 11997 | - * Add a data source
|
|---|
| 11998 | - *
|
|---|
| 11999 | - * @param currentZoom the zoom
|
|---|
| 12000 | - * @param dataSource The datasource to add at the zoom level
|
|---|
| 12001 | - */
|
|---|
| 12002 | - public void addDataSource(int currentZoom, DataSource dataSource) {
|
|---|
| 12003 | - final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 12004 | - dataStore.addDataSource(dataSource);
|
|---|
| 12005 | + // TODO
|
|---|
| 12006 | + return Collections.emptyList();
|
|---|
| 12007 | }
|
|---|
| 12008 |
|
|---|
| 12009 | @Override
|
|---|
| 12010 | @@ -153,23 +136,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12011 |
|
|---|
| 12012 | @Override
|
|---|
| 12013 | public void addPrimitive(VectorPrimitive primitive) {
|
|---|
| 12014 | - primitive.setDataSet(this);
|
|---|
| 12015 | - final int currentZoom = this.zoom;
|
|---|
| 12016 | - final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 12017 | - tryWrite(dataStore, () -> dataStore.addPrimitive(primitive));
|
|---|
| 12018 | - }
|
|---|
| 12019 | -
|
|---|
| 12020 | - /**
|
|---|
| 12021 | - * Remove a primitive from this dataset
|
|---|
| 12022 | - *
|
|---|
| 12023 | - * @param primitive The primitive to remove
|
|---|
| 12024 | - */
|
|---|
| 12025 | - protected void removePrimitive(VectorPrimitive primitive) {
|
|---|
| 12026 | - if (primitive.getDataSet() == this) {
|
|---|
| 12027 | - primitive.setDataSet(null);
|
|---|
| 12028 | - this.dataStoreMap.values()
|
|---|
| 12029 | - .forEach(vectorDataStore -> tryWrite(vectorDataStore, () -> vectorDataStore.removePrimitive(primitive)));
|
|---|
| 12030 | - }
|
|---|
| 12031 | + throw new UnsupportedOperationException("Custom vector primitives are not currently supported");
|
|---|
| 12032 | }
|
|---|
| 12033 |
|
|---|
| 12034 | @Override
|
|---|
| 12035 | @@ -181,59 +148,108 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12036 |
|
|---|
| 12037 | @Override
|
|---|
| 12038 | public List<VectorNode> searchNodes(BBox bbox) {
|
|---|
| 12039 | - return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.searchNodes(bbox))
|
|---|
| 12040 | - .orElseGet(Collections::emptyList);
|
|---|
| 12041 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12042 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12043 | + if (dataStore != null) {
|
|---|
| 12044 | + return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 12045 | + .flatMap(store -> store.searchNodes(bbox).stream()).collect(Collectors.toList());
|
|---|
| 12046 | + }
|
|---|
| 12047 | + return null;
|
|---|
| 12048 | + }).orElseGet(Collections::emptyList);
|
|---|
| 12049 | }
|
|---|
| 12050 |
|
|---|
| 12051 | @Override
|
|---|
| 12052 | public boolean containsNode(VectorNode vectorNode) {
|
|---|
| 12053 | - return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.containsNode(vectorNode)).orElse(false);
|
|---|
| 12054 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12055 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12056 | + return dataStore != null &&
|
|---|
| 12057 | + dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 12058 | + .anyMatch(store -> store.containsNode(vectorNode));
|
|---|
| 12059 | + }).orElse(Boolean.FALSE);
|
|---|
| 12060 | }
|
|---|
| 12061 |
|
|---|
| 12062 | @Override
|
|---|
| 12063 | public List<VectorWay> searchWays(BBox bbox) {
|
|---|
| 12064 | - return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.searchWays(bbox))
|
|---|
| 12065 | - .orElseGet(Collections::emptyList);
|
|---|
| 12066 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12067 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12068 | + if (dataStore != null) {
|
|---|
| 12069 | + return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 12070 | + .flatMap(store -> store.searchWays(bbox).stream()).collect(Collectors.toList());
|
|---|
| 12071 | + }
|
|---|
| 12072 | + return null;
|
|---|
| 12073 | + }).orElseGet(Collections::emptyList);
|
|---|
| 12074 | }
|
|---|
| 12075 |
|
|---|
| 12076 | @Override
|
|---|
| 12077 | public boolean containsWay(VectorWay vectorWay) {
|
|---|
| 12078 | - return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.containsWay(vectorWay)).orElse(false);
|
|---|
| 12079 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12080 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12081 | + return dataStore != null &&
|
|---|
| 12082 | + dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 12083 | + .anyMatch(store -> store.containsWay(vectorWay));
|
|---|
| 12084 | + }).orElse(Boolean.FALSE);
|
|---|
| 12085 | }
|
|---|
| 12086 |
|
|---|
| 12087 | @Override
|
|---|
| 12088 | public List<VectorRelation> searchRelations(BBox bbox) {
|
|---|
| 12089 | - return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.searchRelations(bbox))
|
|---|
| 12090 | - .orElseGet(Collections::emptyList);
|
|---|
| 12091 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12092 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12093 | + if (dataStore != null) {
|
|---|
| 12094 | + return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 12095 | + .flatMap(store -> store.searchRelations(bbox).stream()).collect(Collectors.toList());
|
|---|
| 12096 | + }
|
|---|
| 12097 | + return null;
|
|---|
| 12098 | + }).orElseGet(Collections::emptyList);
|
|---|
| 12099 | }
|
|---|
| 12100 |
|
|---|
| 12101 | @Override
|
|---|
| 12102 | public boolean containsRelation(VectorRelation vectorRelation) {
|
|---|
| 12103 | - return this.getBestZoomDataStore().map(VectorDataStore::getStore).map(store -> store.containsRelation(vectorRelation)).orElse(false);
|
|---|
| 12104 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12105 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12106 | + return dataStore != null &&
|
|---|
| 12107 | + dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 12108 | + .anyMatch(store -> store.containsRelation(vectorRelation));
|
|---|
| 12109 | + }).orElse(Boolean.FALSE);
|
|---|
| 12110 | }
|
|---|
| 12111 |
|
|---|
| 12112 | + /**
|
|---|
| 12113 | + * Get a primitive for an id
|
|---|
| 12114 | + * @param primitiveId type and uniqueId of the primitive. Might be < 0 for newly created primitives
|
|---|
| 12115 | + * @return The primitive for the id. Please note that since this is vector data, there may be more primitives with this id.
|
|---|
| 12116 | + * Please use {@link #getPrimitivesById(PrimitiveId...)} to get all primitives for that {@link PrimitiveId}.
|
|---|
| 12117 | + */
|
|---|
| 12118 | @Override
|
|---|
| 12119 | public VectorPrimitive getPrimitiveById(PrimitiveId primitiveId) {
|
|---|
| 12120 | - return this.getBestZoomDataStore().map(VectorDataStore::getPrimitivesMap).map(m -> m .get(primitiveId)).orElse(null);
|
|---|
| 12121 | + return this.getPrimitivesById(primitiveId).findFirst().orElse(null);
|
|---|
| 12122 | + }
|
|---|
| 12123 | +
|
|---|
| 12124 | + /**
|
|---|
| 12125 | + * Get all primitives for ids
|
|---|
| 12126 | + * @param primitiveIds The ids to search for
|
|---|
| 12127 | + * @return The primitives for the ids (note: as this is vector data, a {@link PrimitiveId} may have multiple associated primitives)
|
|---|
| 12128 | + */
|
|---|
| 12129 | + public Stream<VectorPrimitive> getPrimitivesById(PrimitiveId... primitiveIds) {
|
|---|
| 12130 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12131 | + if (dataStore != null) {
|
|---|
| 12132 | + return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getPrimitivesMap)
|
|---|
| 12133 | + .flatMap(m -> Stream.of(primitiveIds).map(m::get));
|
|---|
| 12134 | + }
|
|---|
| 12135 | + return Stream.empty();
|
|---|
| 12136 | }
|
|---|
| 12137 |
|
|---|
| 12138 | - // The last return statement is "unchecked", even though it is literally the same as the previous return, except
|
|---|
| 12139 | - // as an optional.
|
|---|
| 12140 | - @SuppressWarnings("unchecked")
|
|---|
| 12141 | @Override
|
|---|
| 12142 | public <T extends VectorPrimitive> Collection<T> getPrimitives(
|
|---|
| 12143 | Predicate<? super VectorPrimitive> predicate) {
|
|---|
| 12144 | - final VectorDataStore dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12145 | - if (dataStore == null) {
|
|---|
| 12146 | - return Collections.emptyList();
|
|---|
| 12147 | - }
|
|---|
| 12148 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12149 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12150 | + if (dataStore == null) {
|
|---|
| 12151 | + return null;
|
|---|
| 12152 | + }
|
|---|
| 12153 |
|
|---|
| 12154 | - if (dataStore.getReadWriteLock().isWriteLocked()) {
|
|---|
| 12155 | - return new SubclassFilteredCollection<>(new HashSet<>(dataStore.getAllPrimitives()), predicate);
|
|---|
| 12156 | - }
|
|---|
| 12157 | - return (Collection<T>) tryRead(dataStore, () -> new SubclassFilteredCollection<>(dataStore.getAllPrimitives(), predicate))
|
|---|
| 12158 | - // Throw an NPE if we don't have a collection (this should never happen, so if it does, _something_ is wrong)
|
|---|
| 12159 | - .orElseThrow(NullPointerException::new);
|
|---|
| 12160 | + // This cast is needed (otherwise, Collections.emptyList doesn't compile)
|
|---|
| 12161 | + return (Collection<T>) new SubclassFilteredCollection<>(dataStore.stream().map(MVTTile::getData)
|
|---|
| 12162 | + .map(VectorDataStore::getAllPrimitives).flatMap(Collection::stream).distinct().collect(Collectors.toList()), predicate);
|
|---|
| 12163 | + }).orElseGet(Collections::emptyList);
|
|---|
| 12164 | }
|
|---|
| 12165 |
|
|---|
| 12166 | @Override
|
|---|
| 12167 | @@ -278,8 +294,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12168 | */
|
|---|
| 12169 | @Override
|
|---|
| 12170 | public Lock getReadLock() {
|
|---|
| 12171 | - return getBestZoomDataStore().map(VectorDataStore::getReadWriteLock).map(ReentrantReadWriteLock::readLock)
|
|---|
| 12172 | - .orElse(this.readWriteLock.readLock());
|
|---|
| 12173 | + return this.readWriteLock.readLock();
|
|---|
| 12174 | }
|
|---|
| 12175 |
|
|---|
| 12176 | @Override
|
|---|
| 12177 | @@ -336,21 +351,28 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12178 |
|
|---|
| 12179 | @Override
|
|---|
| 12180 | public Collection<VectorPrimitive> getAllSelected() {
|
|---|
| 12181 | - final Map<PrimitiveId, VectorPrimitive> dataStore = this.getBestZoomDataStore().map(VectorDataStore::getPrimitivesMap).orElse(null);
|
|---|
| 12182 | - if (dataStore != null) {
|
|---|
| 12183 | - // The dataStore is a final variable from the VectorDataStore.
|
|---|
| 12184 | - synchronized (dataStore) {
|
|---|
| 12185 | - return this.currentSelectedPrimitives.stream().map(dataStore::get).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 12186 | + return tryRead(this.readWriteLock, () -> {
|
|---|
| 12187 | + final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12188 | + if (dataStore != null) {
|
|---|
| 12189 | + // The dataStore is what we don't want to concurrently modify
|
|---|
| 12190 | + synchronized (dataStore) {
|
|---|
| 12191 | + return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getPrimitivesMap).flatMap(dataMap -> {
|
|---|
| 12192 | + // Synchronize on dataMap to avoid concurrent modification errors
|
|---|
| 12193 | + synchronized (dataMap) {
|
|---|
| 12194 | + return this.currentSelectedPrimitives.stream().map(dataMap::get).filter(Objects::nonNull);
|
|---|
| 12195 | + }
|
|---|
| 12196 | + }).collect(Collectors.toList());
|
|---|
| 12197 | + }
|
|---|
| 12198 | }
|
|---|
| 12199 | - }
|
|---|
| 12200 | - return Collections.emptyList();
|
|---|
| 12201 | + return null;
|
|---|
| 12202 | + }).orElseGet(Collections::emptyList);
|
|---|
| 12203 | }
|
|---|
| 12204 |
|
|---|
| 12205 | /**
|
|---|
| 12206 | * Get the best zoom datastore
|
|---|
| 12207 | * @return A datastore with data, or {@code null} if no good datastore exists.
|
|---|
| 12208 | */
|
|---|
| 12209 | - private Optional<VectorDataStore> getBestZoomDataStore() {
|
|---|
| 12210 | + private Optional<Storage<MVTTile>> getBestZoomDataStore() {
|
|---|
| 12211 | final int currentZoom = this.zoom;
|
|---|
| 12212 | if (this.dataStoreMap.containsKey(currentZoom)) {
|
|---|
| 12213 | return Optional.of(this.dataStoreMap.get(currentZoom));
|
|---|
| 12214 | @@ -519,11 +541,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12215 | nearestZoom[2] = keys[index + 1];
|
|---|
| 12216 | }
|
|---|
| 12217 |
|
|---|
| 12218 | - nearestZoom[3] = this.getBestZoomDataStore().map(VectorDataStore::getZoom).orElse(-1);
|
|---|
| 12219 | - IntStream.of(keys).filter(key -> IntStream.of(nearestZoom).noneMatch(zoomKey -> zoomKey == key))
|
|---|
| 12220 | - .mapToObj(this.dataStoreMap::get).forEach(VectorDataStore::destroy);
|
|---|
| 12221 | - IntStream.of(keys).filter(key -> IntStream.of(nearestZoom).noneMatch(zoomKey -> zoomKey == key))
|
|---|
| 12222 | - .forEach(this.dataStoreMap::remove);
|
|---|
| 12223 | + // TODO cleanup zooms for memory
|
|---|
| 12224 | }
|
|---|
| 12225 | }
|
|---|
| 12226 |
|
|---|
| 12227 | @@ -534,13 +552,15 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12228 | /**
|
|---|
| 12229 | * Add tile data to this dataset
|
|---|
| 12230 | * @param tile The tile to add
|
|---|
| 12231 | - * @param <T> The tile type
|
|---|
| 12232 | */
|
|---|
| 12233 | - public <T extends Tile & VectorTile> void addTileData(T tile) {
|
|---|
| 12234 | - final int currentZoom = tile.getZoom();
|
|---|
| 12235 | - // computeIfAbsent should be thread safe (ConcurrentHashMap indicates it is, anyway)
|
|---|
| 12236 | - final VectorDataStore dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new VectorDataStore(this, tZoom));
|
|---|
| 12237 | - dataStore.addTile(tile, this.invisibleLayers);
|
|---|
| 12238 | + public void addTileData(MVTTile tile) {
|
|---|
| 12239 | + tryWrite(this.readWriteLock, () -> {
|
|---|
| 12240 | + final int currentZoom = tile.getZoom();
|
|---|
| 12241 | + // computeIfAbsent should be thread safe (ConcurrentHashMap indicates it is, anyway)
|
|---|
| 12242 | + final Storage<MVTTile> dataStore = this.dataStoreMap.computeIfAbsent(currentZoom, tZoom -> new Storage<>());
|
|---|
| 12243 | + tile.getData().getAllPrimitives().forEach(primitive -> primitive.setDataSet(this));
|
|---|
| 12244 | + dataStore.add(tile);
|
|---|
| 12245 | + });
|
|---|
| 12246 | }
|
|---|
| 12247 |
|
|---|
| 12248 | /**
|
|---|
| 12249 | @@ -550,15 +570,15 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12250 | * @param <T> The return type
|
|---|
| 12251 | * @return The optional return
|
|---|
| 12252 | */
|
|---|
| 12253 | - private static <T> Optional<T> tryRead(VectorDataStore dataStore, Supplier<T> supplier) {
|
|---|
| 12254 | + private static <T> Optional<T> tryRead(ReentrantReadWriteLock lock, Supplier<T> supplier) {
|
|---|
| 12255 | try {
|
|---|
| 12256 | - dataStore.getReadWriteLock().readLock().lockInterruptibly();
|
|---|
| 12257 | + lock.readLock().lockInterruptibly();
|
|---|
| 12258 | return Optional.ofNullable(supplier.get());
|
|---|
| 12259 | } catch (InterruptedException e) {
|
|---|
| 12260 | Logging.error(e);
|
|---|
| 12261 | Thread.currentThread().interrupt();
|
|---|
| 12262 | } finally {
|
|---|
| 12263 | - dataStore.getReadWriteLock().readLock().unlock();
|
|---|
| 12264 | + lock.readLock().unlock();
|
|---|
| 12265 | }
|
|---|
| 12266 | return Optional.empty();
|
|---|
| 12267 | }
|
|---|
| 12268 | @@ -568,16 +588,16 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12269 | *
|
|---|
| 12270 | * @param runnable The writing function
|
|---|
| 12271 | */
|
|---|
| 12272 | - private static void tryWrite(VectorDataStore dataStore, Runnable runnable) {
|
|---|
| 12273 | + private static void tryWrite(ReentrantReadWriteLock lock, Runnable runnable) {
|
|---|
| 12274 | try {
|
|---|
| 12275 | - dataStore.getReadWriteLock().writeLock().lockInterruptibly();
|
|---|
| 12276 | + lock.writeLock().lockInterruptibly();
|
|---|
| 12277 | runnable.run();
|
|---|
| 12278 | } catch (InterruptedException e) {
|
|---|
| 12279 | Logging.error(e);
|
|---|
| 12280 | Thread.currentThread().interrupt();
|
|---|
| 12281 | } finally {
|
|---|
| 12282 | - if (dataStore.getReadWriteLock().isWriteLockedByCurrentThread()) {
|
|---|
| 12283 | - dataStore.getReadWriteLock().writeLock().unlock();
|
|---|
| 12284 | + if (lock.isWriteLockedByCurrentThread()) {
|
|---|
| 12285 | + lock.writeLock().unlock();
|
|---|
| 12286 | }
|
|---|
| 12287 | }
|
|---|
| 12288 | }
|
|---|
| 12289 | @@ -610,16 +630,11 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12290 | * @param invisibleLayers The layer to not show
|
|---|
| 12291 | */
|
|---|
| 12292 | public void setInvisibleLayers(Collection<String> invisibleLayers) {
|
|---|
| 12293 | - if (invisibleLayers == null || invisibleLayers.isEmpty()
|
|---|
| 12294 | - || invisibleLayers.stream().filter(Objects::nonNull).allMatch(String::isEmpty)) {
|
|---|
| 12295 | - this.invisibleLayers = NO_INVISIBLE_LAYERS;
|
|---|
| 12296 | - return;
|
|---|
| 12297 | - }
|
|---|
| 12298 | String[] currentInvisibleLayers = invisibleLayers.stream().filter(Objects::nonNull).toArray(String[]::new);
|
|---|
| 12299 | - this.invisibleLayers = currentInvisibleLayers;
|
|---|
| 12300 | List<String> temporaryList = Arrays.asList(currentInvisibleLayers);
|
|---|
| 12301 | - this.dataStoreMap.values().forEach(dataStore -> dataStore.getAllPrimitives().parallelStream()
|
|---|
| 12302 | - .forEach(primitive -> primitive.setVisible(!temporaryList.contains(primitive.getLayer()))));
|
|---|
| 12303 | + this.dataStoreMap.values().stream().flatMap(Collection::stream).map(MVTTile::getData)
|
|---|
| 12304 | + .forEach(dataStore -> dataStore.getAllPrimitives().parallelStream()
|
|---|
| 12305 | + .forEach(primitive -> primitive.setVisible(!temporaryList.contains(primitive.getLayer()))));
|
|---|
| 12306 | }
|
|---|
| 12307 |
|
|---|
| 12308 | @Override
|
|---|
| 12309 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 12310 | index 7e48e4313..dceef3b8e 100644
|
|---|
| 12311 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 12312 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 12313 | @@ -4,6 +4,7 @@ package org.openstreetmap.josm.data.vector;
|
|---|
| 12314 | import org.openstreetmap.gui.jmapviewer.Coordinate;
|
|---|
| 12315 | import org.openstreetmap.gui.jmapviewer.Tile;
|
|---|
| 12316 | import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
|---|
| 12317 | +import org.openstreetmap.josm.data.IQuadBucketType;
|
|---|
| 12318 | import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 12319 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 12320 | import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 12321 | @@ -25,12 +26,10 @@ import java.awt.geom.Ellipse2D;
|
|---|
| 12322 | import java.awt.geom.Path2D;
|
|---|
| 12323 | import java.awt.geom.PathIterator;
|
|---|
| 12324 | import java.util.ArrayList;
|
|---|
| 12325 | -import java.util.Arrays;
|
|---|
| 12326 | import java.util.Collection;
|
|---|
| 12327 | import java.util.Collections;
|
|---|
| 12328 | import java.util.List;
|
|---|
| 12329 | import java.util.Objects;
|
|---|
| 12330 | -import java.util.Optional;
|
|---|
| 12331 | import java.util.stream.Collectors;
|
|---|
| 12332 |
|
|---|
| 12333 | /**
|
|---|
| 12334 | @@ -38,20 +37,12 @@ import java.util.stream.Collectors;
|
|---|
| 12335 | * @author Taylor Smock
|
|---|
| 12336 | * @since xxx
|
|---|
| 12337 | */
|
|---|
| 12338 | -class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay, VectorRelation> implements Destroyable {
|
|---|
| 12339 | - private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|---|
| 12340 | +public class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay, VectorRelation> implements Destroyable {
|
|---|
| 12341 | private static final String JOSM_MERGE_TYPE_KEY = "josm_merge_type";
|
|---|
| 12342 | private static final String ORIGINAL_ID = "original_id";
|
|---|
| 12343 | - private final VectorDataSet dataSet;
|
|---|
| 12344 | -
|
|---|
| 12345 | - VectorDataStore(VectorDataSet dataSet, int zoom) {
|
|---|
| 12346 | - super(zoom);
|
|---|
| 12347 | - this.dataSet = dataSet;
|
|---|
| 12348 | - }
|
|---|
| 12349 |
|
|---|
| 12350 | @Override
|
|---|
| 12351 | protected void addPrimitive(VectorPrimitive primitive) {
|
|---|
| 12352 | - primitive.setDataSet(this.dataSet);
|
|---|
| 12353 | // The field is uint64, so we can use negative numbers to indicate that it is a "generated" object (e.g., nodes for ways)
|
|---|
| 12354 | if (primitive.getUniqueId() == 0) {
|
|---|
| 12355 | final UniqueIdGenerator generator = primitive.getIdGenerator();
|
|---|
| 12356 | @@ -92,7 +83,6 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 12357 | temporaryRelation.addRelationMember(new VectorRelationMember("", alreadyAdded));
|
|---|
| 12358 | }
|
|---|
| 12359 | temporaryRelation.addRelationMember(new VectorRelationMember("", primitive));
|
|---|
| 12360 | - temporaryRelation.setDataSet(this.dataSet);
|
|---|
| 12361 | super.addPrimitive(primitive);
|
|---|
| 12362 | super.addPrimitive(temporaryRelation);
|
|---|
| 12363 | }
|
|---|
| 12364 | @@ -161,13 +151,21 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 12365 |
|
|---|
| 12366 | private synchronized <T extends Tile & VectorTile> VectorNode pointToNode(T tile, Layer layer,
|
|---|
| 12367 | Collection<VectorPrimitive> featureObjects, int x, int y) {
|
|---|
| 12368 | - final ICoordinate upperLeft = tile.getTileSource().tileXYToLatLon(tile);
|
|---|
| 12369 | + final BBox tileBbox;
|
|---|
| 12370 | + if (tile instanceof IQuadBucketType) {
|
|---|
| 12371 | + tileBbox = ((IQuadBucketType) tile).getBBox();
|
|---|
| 12372 | + } else {
|
|---|
| 12373 | + final ICoordinate upperLeft = tile.getTileSource().tileXYToLatLon(tile);
|
|---|
| 12374 | + final ICoordinate lowerRight = tile.getTileSource()
|
|---|
| 12375 | + .tileXYToLatLon(tile.getXtile() + 1, tile.getYtile() + 1, tile.getZoom());
|
|---|
| 12376 | +
|
|---|
| 12377 | + tileBbox = new BBox(upperLeft.getLon(), upperLeft.getLat(), lowerRight.getLon(), lowerRight.getLat());
|
|---|
| 12378 | + }
|
|---|
| 12379 | final int layerExtent = layer.getExtent();
|
|---|
| 12380 | - final ICoordinate lowerRight = tile.getTileSource()
|
|---|
| 12381 | - .tileXYToLatLon(tile.getXtile() + 1, tile.getYtile() + 1, tile.getZoom());
|
|---|
| 12382 | final ICoordinate coords = new Coordinate(
|
|---|
| 12383 | - upperLeft.getLat() - (upperLeft.getLat() - lowerRight.getLat()) * y / layerExtent,
|
|---|
| 12384 | - upperLeft.getLon() + (lowerRight.getLon() - upperLeft.getLon()) * x / layerExtent);
|
|---|
| 12385 | + tileBbox.getMaxLat() - (tileBbox.getMaxLat() - tileBbox.getMinLat()) * y / layerExtent,
|
|---|
| 12386 | + tileBbox.getMinLon() + (tileBbox.getMaxLon() - tileBbox.getMinLon()) * x / layerExtent
|
|---|
| 12387 | + );
|
|---|
| 12388 | final Collection<VectorNode> nodes = this.store
|
|---|
| 12389 | .searchNodes(new BBox(coords.getLon(), coords.getLat(), VectorDataSet.DUPE_NODE_DISTANCE));
|
|---|
| 12390 | final VectorNode node;
|
|---|
| 12391 | @@ -273,48 +271,11 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 12392 | }
|
|---|
| 12393 |
|
|---|
| 12394 | /**
|
|---|
| 12395 | - * Add a tile to this data store
|
|---|
| 12396 | - * @param <T> The tile type
|
|---|
| 12397 | + * Add the information from a tile to this object
|
|---|
| 12398 | * @param tile The tile to add
|
|---|
| 12399 | - */
|
|---|
| 12400 | - public synchronized <T extends Tile & VectorTile> void addTile(T tile) {
|
|---|
| 12401 | - addTile(tile, EMPTY_STRING_ARRAY);
|
|---|
| 12402 | - }
|
|---|
| 12403 | -
|
|---|
| 12404 | - /**
|
|---|
| 12405 | - * Add a tile to this data store
|
|---|
| 12406 | * @param <T> The tile type
|
|---|
| 12407 | - * @param tile The tile to add
|
|---|
| 12408 | - * @param invisibleLayers Any invisible current invisible layers
|
|---|
| 12409 | */
|
|---|
| 12410 | - public <T extends Tile & VectorTile> void addTile(T tile, String[] invisibleLayers) {
|
|---|
| 12411 | - List<String> invisibleLayerList = Arrays.asList(invisibleLayers);
|
|---|
| 12412 | - Optional<Tile> previous;
|
|---|
| 12413 | - synchronized (this.addedTiles) {
|
|---|
| 12414 | - previous = this.addedTiles.stream()
|
|---|
| 12415 | - .filter(t -> t.getTileXY().equals(tile.getTileXY()) && t.getZoom() == tile.getZoom()).findAny();
|
|---|
| 12416 | - }
|
|---|
| 12417 | - // Check if we have already added the tile (just to save processing time)
|
|---|
| 12418 | - if (!previous.isPresent() || (!previous.get().isLoaded() && !previous.get().isLoading())) {
|
|---|
| 12419 | - previous.ifPresent(this.addedTiles::remove);
|
|---|
| 12420 | - this.addedTiles.add(tile);
|
|---|
| 12421 | - VectorDataStore tStore = new VectorDataStore(this.dataSet, this.zoom);
|
|---|
| 12422 | - tStore.createDataTile(tile, invisibleLayerList);
|
|---|
| 12423 | - try {
|
|---|
| 12424 | - this.getReadWriteLock().writeLock().lockInterruptibly();
|
|---|
| 12425 | - tStore.getAllPrimitives().forEach(this::addPrimitive);
|
|---|
| 12426 | - } catch (InterruptedException e) {
|
|---|
| 12427 | - Logging.error(e);
|
|---|
| 12428 | - Thread.currentThread().interrupt();
|
|---|
| 12429 | - } finally {
|
|---|
| 12430 | - if (this.getReadWriteLock().isWriteLockedByCurrentThread()) {
|
|---|
| 12431 | - this.getReadWriteLock().writeLock().unlock();
|
|---|
| 12432 | - }
|
|---|
| 12433 | - }
|
|---|
| 12434 | - }
|
|---|
| 12435 | - }
|
|---|
| 12436 | -
|
|---|
| 12437 | - private <T extends Tile & VectorTile> void createDataTile(T tile, List<String> invisibleLayerList) {
|
|---|
| 12438 | + public <T extends Tile & VectorTile> void addDataTile(T tile) {
|
|---|
| 12439 | for (Layer layer : tile.getLayers()) {
|
|---|
| 12440 | layer.getFeatures().forEach(feature -> {
|
|---|
| 12441 | org.openstreetmap.josm.data.imagery.vectortile.mapbox.Geometry geometry = feature
|
|---|
| 12442 | @@ -363,11 +324,6 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 12443 | feature.getTags().forEach(primitive::put);
|
|---|
| 12444 | featureObjects.forEach(this::addPrimitive);
|
|---|
| 12445 | primaryFeatureObjects.forEach(this::addPrimitive);
|
|---|
| 12446 | - if (invisibleLayerList.contains(primitive.getLayer())) {
|
|---|
| 12447 | - primitive.setVisible(false);
|
|---|
| 12448 | - featureObjects.forEach(p -> p.setVisible(false));
|
|---|
| 12449 | - primaryFeatureObjects.forEach(p -> p.setVisible(false));
|
|---|
| 12450 | - }
|
|---|
| 12451 | try {
|
|---|
| 12452 | this.addPrimitive(primitive);
|
|---|
| 12453 | } catch (JosmRuntimeException e) {
|
|---|
| 12454 | @@ -377,8 +333,8 @@ class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay,
|
|---|
| 12455 | });
|
|---|
| 12456 | }
|
|---|
| 12457 | // Replace original_ids with the same object (reduce memory usage)
|
|---|
| 12458 | - // Strings aren't interned automatically (see
|
|---|
| 12459 | - Collection<IPrimitive> primitives = this.dataSet.allPrimitives().stream().filter(p -> p.hasKey(ORIGINAL_ID))
|
|---|
| 12460 | + // Strings aren't interned automatically in some GC implementations
|
|---|
| 12461 | + Collection<IPrimitive> primitives = this.getAllPrimitives().stream().filter(p -> p.hasKey(ORIGINAL_ID))
|
|---|
| 12462 | .collect(Collectors.toList());
|
|---|
| 12463 | List<String> toReplace = primitives.stream().map(p -> p.get(ORIGINAL_ID)).filter(Objects::nonNull).collect(Collectors.toList());
|
|---|
| 12464 | primitives.stream().filter(p -> toReplace.contains(p.get(ORIGINAL_ID)))
|
|---|
| 12465 | --
|
|---|
| 12466 | GitLab
|
|---|
| 12467 |
|
|---|
| 12468 |
|
|---|
| 12469 | From cf36cba786589d699b6b859b173f730fcf2d945f Mon Sep 17 00:00:00 2001
|
|---|
| 12470 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 12471 | Date: Thu, 29 Apr 2021 13:21:57 -0600
|
|---|
| 12472 | Subject: [PATCH 25/50] FIXUP: NPE would occur if a null object was passed to
|
|---|
| 12473 | in a selection for Vector data
|
|---|
| 12474 |
|
|---|
| 12475 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 12476 | ---
|
|---|
| 12477 | src/org/openstreetmap/josm/data/vector/VectorDataSet.java | 2 +-
|
|---|
| 12478 | 1 file changed, 1 insertion(+), 1 deletion(-)
|
|---|
| 12479 |
|
|---|
| 12480 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 12481 | index 55366d555..dc79255e7 100644
|
|---|
| 12482 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 12483 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 12484 | @@ -435,7 +435,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12485 |
|
|---|
| 12486 | private void setSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 12487 | this.doSelectionChange(old -> new IDataSelectionListener.SelectionReplaceEvent<>(this, old,
|
|---|
| 12488 | - osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 12489 | + osm.filter(Objects::nonNull).map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 12490 | }
|
|---|
| 12491 |
|
|---|
| 12492 | @Override
|
|---|
| 12493 | --
|
|---|
| 12494 | GitLab
|
|---|
| 12495 |
|
|---|
| 12496 |
|
|---|
| 12497 | From aca31077b866efff187fdf61848fdd73a8722519 Mon Sep 17 00:00:00 2001
|
|---|
| 12498 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 12499 | Date: Thu, 29 Apr 2021 14:50:46 -0600
|
|---|
| 12500 | Subject: [PATCH 26/50] VectorDataSet: Fix an NPE
|
|---|
| 12501 |
|
|---|
| 12502 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 12503 | ---
|
|---|
| 12504 | src/org/openstreetmap/josm/data/vector/VectorDataSet.java | 2 +-
|
|---|
| 12505 | 1 file changed, 1 insertion(+), 1 deletion(-)
|
|---|
| 12506 |
|
|---|
| 12507 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 12508 | index dc79255e7..1541d169b 100644
|
|---|
| 12509 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 12510 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 12511 | @@ -232,7 +232,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 12512 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 12513 | if (dataStore != null) {
|
|---|
| 12514 | return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getPrimitivesMap)
|
|---|
| 12515 | - .flatMap(m -> Stream.of(primitiveIds).map(m::get));
|
|---|
| 12516 | + .flatMap(m -> Stream.of(primitiveIds).map(m::get)).filter(Objects::nonNull);
|
|---|
| 12517 | }
|
|---|
| 12518 | return Stream.empty();
|
|---|
| 12519 | }
|
|---|
| 12520 | --
|
|---|
| 12521 | GitLab
|
|---|
| 12522 |
|
|---|
| 12523 |
|
|---|
| 12524 | From 260113af133abd968e0af19821cb082c7490ba94 Mon Sep 17 00:00:00 2001
|
|---|
| 12525 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 12526 | Date: Thu, 29 Apr 2021 15:35:26 -0600
|
|---|
| 12527 | Subject: [PATCH 27/50] OsmData: Use I<OsmType>.class for get<OsmType> instead
|
|---|
| 12528 | of <OsmType>.class
|
|---|
| 12529 |
|
|---|
| 12530 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 12531 | ---
|
|---|
| 12532 | src/org/openstreetmap/josm/data/osm/OsmData.java | 6 +++---
|
|---|
| 12533 | 1 file changed, 3 insertions(+), 3 deletions(-)
|
|---|
| 12534 |
|
|---|
| 12535 | diff --git a/src/org/openstreetmap/josm/data/osm/OsmData.java b/src/org/openstreetmap/josm/data/osm/OsmData.java
|
|---|
| 12536 | index a96515a53..a5be8be9a 100644
|
|---|
| 12537 | --- a/src/org/openstreetmap/josm/data/osm/OsmData.java
|
|---|
| 12538 | +++ b/src/org/openstreetmap/josm/data/osm/OsmData.java
|
|---|
| 12539 | @@ -350,7 +350,7 @@ public interface OsmData<O extends IPrimitive, N extends INode, W extends IWay<N
|
|---|
| 12540 | * @return selected nodes
|
|---|
| 12541 | */
|
|---|
| 12542 | default Collection<N> getSelectedNodes() {
|
|---|
| 12543 | - return new SubclassFilteredCollection<>(getSelected(), Node.class::isInstance);
|
|---|
| 12544 | + return new SubclassFilteredCollection<>(getSelected(), INode.class::isInstance);
|
|---|
| 12545 | }
|
|---|
| 12546 |
|
|---|
| 12547 | /**
|
|---|
| 12548 | @@ -358,7 +358,7 @@ public interface OsmData<O extends IPrimitive, N extends INode, W extends IWay<N
|
|---|
| 12549 | * @return selected ways
|
|---|
| 12550 | */
|
|---|
| 12551 | default Collection<W> getSelectedWays() {
|
|---|
| 12552 | - return new SubclassFilteredCollection<>(getSelected(), Way.class::isInstance);
|
|---|
| 12553 | + return new SubclassFilteredCollection<>(getSelected(), IWay.class::isInstance);
|
|---|
| 12554 | }
|
|---|
| 12555 |
|
|---|
| 12556 | /**
|
|---|
| 12557 | @@ -366,7 +366,7 @@ public interface OsmData<O extends IPrimitive, N extends INode, W extends IWay<N
|
|---|
| 12558 | * @return selected relations
|
|---|
| 12559 | */
|
|---|
| 12560 | default Collection<R> getSelectedRelations() {
|
|---|
| 12561 | - return new SubclassFilteredCollection<>(getSelected(), Relation.class::isInstance);
|
|---|
| 12562 | + return new SubclassFilteredCollection<>(getSelected(), IRelation.class::isInstance);
|
|---|
| 12563 | }
|
|---|
| 12564 |
|
|---|
| 12565 | /**
|
|---|
| 12566 | --
|
|---|
| 12567 | GitLab
|
|---|
| 12568 |
|
|---|
| 12569 |
|
|---|
| 12570 | From 6f411f7993cdfd4809c9015bdec17fdaf40c7009 Mon Sep 17 00:00:00 2001
|
|---|
| 12571 | From: Simon Legner <simon.legner@gmail.com>
|
|---|
| 12572 | Date: Sat, 1 May 2021 22:36:37 +0000
|
|---|
| 12573 | Subject: [PATCH 28/50] MapBox -> Mapbox (official spelling) by simon04
|
|---|
| 12574 |
|
|---|
| 12575 | ---
|
|---|
| 12576 | src/org/openstreetmap/josm/data/imagery/ImageryInfo.java | 2 +-
|
|---|
| 12577 | .../josm/data/imagery/vectortile/mapbox/style/Expression.java | 2 +-
|
|---|
| 12578 | .../data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java | 2 +-
|
|---|
| 12579 | 3 files changed, 3 insertions(+), 3 deletions(-)
|
|---|
| 12580 |
|
|---|
| 12581 | diff --git a/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java b/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
|
|---|
| 12582 | index 32b1055ed..8a93da2c2 100644
|
|---|
| 12583 | --- a/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
|
|---|
| 12584 | +++ b/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
|
|---|
| 12585 | @@ -62,7 +62,7 @@ public class ImageryInfo extends
|
|---|
| 12586 | WMS_ENDPOINT("wms_endpoint"),
|
|---|
| 12587 | /** WMTS stores GetCapabilities URL. Does not store any information about the layer **/
|
|---|
| 12588 | WMTS("wmts"),
|
|---|
| 12589 | - /** MapBox Vector Tiles entry*/
|
|---|
| 12590 | + /** Mapbox Vector Tiles entry*/
|
|---|
| 12591 | MVT("mvt");
|
|---|
| 12592 |
|
|---|
| 12593 | private final String typeString;
|
|---|
| 12594 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java
|
|---|
| 12595 | index a7f677755..e22f02a45 100644
|
|---|
| 12596 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java
|
|---|
| 12597 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Expression.java
|
|---|
| 12598 | @@ -11,7 +11,7 @@ import javax.json.JsonString;
|
|---|
| 12599 | import javax.json.JsonValue;
|
|---|
| 12600 |
|
|---|
| 12601 | /**
|
|---|
| 12602 | - * A MapBox vector style expression (immutable)
|
|---|
| 12603 | + * A Mapbox vector style expression (immutable)
|
|---|
| 12604 | * @author Taylor Smock
|
|---|
| 12605 | * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/">https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/</a>
|
|---|
| 12606 | * @since xxx
|
|---|
| 12607 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 12608 | index ec24ee5cf..a945cbf57 100644
|
|---|
| 12609 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 12610 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 12611 | @@ -45,7 +45,7 @@ import org.openstreetmap.josm.tools.Logging;
|
|---|
| 12612 | * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/">https://docs.mapbox.com/mapbox-gl-js/style-spec/</a>
|
|---|
| 12613 | * @since xxx
|
|---|
| 12614 | */
|
|---|
| 12615 | -public class MapBoxVectorStyle {
|
|---|
| 12616 | +public class MapboxVectorStyle {
|
|---|
| 12617 |
|
|---|
| 12618 | private static final ConcurrentHashMap<String, MapBoxVectorStyle> STYLE_MAPPING = new ConcurrentHashMap<>();
|
|---|
| 12619 |
|
|---|
| 12620 | --
|
|---|
| 12621 | GitLab
|
|---|
| 12622 |
|
|---|
| 12623 |
|
|---|
| 12624 | From 82292ff8eee5cbc40887c389c86ce1e401ffda43 Mon Sep 17 00:00:00 2001
|
|---|
| 12625 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 12626 | Date: Mon, 3 May 2021 07:54:35 -0600
|
|---|
| 12627 | Subject: [PATCH 29/50] FIXUP: MapBox -> Mapbox
|
|---|
| 12628 |
|
|---|
| 12629 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 12630 | ---
|
|---|
| 12631 | .../data/imagery/vectortile/mapbox/Layer.java | 4 +--
|
|---|
| 12632 | .../imagery/vectortile/mapbox/MVTTile.java | 2 +-
|
|---|
| 12633 | ...java => MapboxVectorCachedTileLoader.java} | 12 ++++-----
|
|---|
| 12634 | ...a => MapboxVectorCachedTileLoaderJob.java} | 8 +++---
|
|---|
| 12635 | .../mapbox/MapboxVectorTileSource.java | 10 +++----
|
|---|
| 12636 | .../vectortile/mapbox/style/Layers.java | 2 +-
|
|---|
| 12637 | ...ectorStyle.java => MapboxVectorStyle.java} | 18 ++++++-------
|
|---|
| 12638 | .../vectortile/mapbox/style/Source.java | 2 +-
|
|---|
| 12639 | .../vectortile/mapbox/style/SourceType.java | 2 +-
|
|---|
| 12640 | .../josm/gui/layer/imagery/MVTLayer.java | 8 +++---
|
|---|
| 12641 | .../preferences/imagery/AddMVTLayerPanel.java | 2 +-
|
|---|
| 12642 | .../vectortile/mapbox/MVTTileTest.java | 4 +--
|
|---|
| 12643 | .../mapbox/MapboxVectorTileSourceTest.java | 4 +--
|
|---|
| 12644 | ...leTest.java => MapboxVectorStyleTest.java} | 26 +++++++++----------
|
|---|
| 12645 | .../josm/data/vector/VectorDataSetTest.java | 10 +++----
|
|---|
| 12646 | 15 files changed, 57 insertions(+), 57 deletions(-)
|
|---|
| 12647 | rename src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/{MapBoxVectorCachedTileLoader.java => MapboxVectorCachedTileLoader.java} (88%)
|
|---|
| 12648 | rename src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/{MapBoxVectorCachedTileLoaderJob.java => MapboxVectorCachedTileLoaderJob.java} (68%)
|
|---|
| 12649 | rename src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/{MapBoxVectorStyle.java => MapboxVectorStyle.java} (95%)
|
|---|
| 12650 | rename test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/{MapBoxVectorStyleTest.java => MapboxVectorStyleTest.java} (95%)
|
|---|
| 12651 |
|
|---|
| 12652 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 12653 | index 0a6bb073e..b0b344da1 100644
|
|---|
| 12654 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 12655 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 12656 | @@ -218,8 +218,8 @@ public final class Layer implements Destroyable {
|
|---|
| 12657 | }
|
|---|
| 12658 |
|
|---|
| 12659 | /**
|
|---|
| 12660 | - * Get the MapBox Vector Tile version specification for this layer
|
|---|
| 12661 | - * @return The version of the MapBox Vector Tile specification
|
|---|
| 12662 | + * Get the Mapbox Vector Tile version specification for this layer
|
|---|
| 12663 | + * @return The version of the Mapbox Vector Tile specification
|
|---|
| 12664 | */
|
|---|
| 12665 | public byte getVersion() {
|
|---|
| 12666 | return this.version;
|
|---|
| 12667 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 12668 | index ab77c43f4..0743bec34 100644
|
|---|
| 12669 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 12670 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 12671 | @@ -22,7 +22,7 @@ import java.util.List;
|
|---|
| 12672 | import java.util.stream.Collectors;
|
|---|
| 12673 |
|
|---|
| 12674 | /**
|
|---|
| 12675 | - * A class for MapBox Vector Tiles
|
|---|
| 12676 | + * A class for Mapbox Vector Tiles
|
|---|
| 12677 | *
|
|---|
| 12678 | * @author Taylor Smock
|
|---|
| 12679 | * @since xxx
|
|---|
| 12680 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoader.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorCachedTileLoader.java
|
|---|
| 12681 | similarity index 88%
|
|---|
| 12682 | rename from src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoader.java
|
|---|
| 12683 | rename to src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorCachedTileLoader.java
|
|---|
| 12684 | index bf1b368d9..abe8d5992 100644
|
|---|
| 12685 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoader.java
|
|---|
| 12686 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorCachedTileLoader.java
|
|---|
| 12687 | @@ -23,7 +23,7 @@ import org.apache.commons.jcs3.access.behavior.ICacheAccess;
|
|---|
| 12688 | * @author Taylor Smock
|
|---|
| 12689 | * @since xxx
|
|---|
| 12690 | */
|
|---|
| 12691 | -public class MapBoxVectorCachedTileLoader implements TileLoader, CachedTileLoader {
|
|---|
| 12692 | +public class MapboxVectorCachedTileLoader implements TileLoader, CachedTileLoader {
|
|---|
| 12693 | protected final ICacheAccess<String, BufferedImageCacheEntry> cache;
|
|---|
| 12694 | protected final TileLoaderListener listener;
|
|---|
| 12695 | protected final TileJobOptions options;
|
|---|
| 12696 | @@ -38,8 +38,8 @@ public class MapBoxVectorCachedTileLoader implements TileLoader, CachedTileLoade
|
|---|
| 12697 | * @param cache of the cache
|
|---|
| 12698 | * @param options tile job options
|
|---|
| 12699 | */
|
|---|
| 12700 | - public MapBoxVectorCachedTileLoader(TileLoaderListener listener, ICacheAccess<String, BufferedImageCacheEntry> cache,
|
|---|
| 12701 | - TileJobOptions options) {
|
|---|
| 12702 | + public MapboxVectorCachedTileLoader(TileLoaderListener listener, ICacheAccess<String, BufferedImageCacheEntry> cache,
|
|---|
| 12703 | + TileJobOptions options) {
|
|---|
| 12704 | CheckParameterUtil.ensureParameterNotNull(cache, "cache");
|
|---|
| 12705 | this.cache = cache;
|
|---|
| 12706 | this.options = options;
|
|---|
| 12707 | @@ -53,7 +53,7 @@ public class MapBoxVectorCachedTileLoader implements TileLoader, CachedTileLoade
|
|---|
| 12708 |
|
|---|
| 12709 | @Override
|
|---|
| 12710 | public TileJob createTileLoaderJob(Tile tile) {
|
|---|
| 12711 | - return new MapBoxVectorCachedTileLoaderJob(
|
|---|
| 12712 | + return new MapboxVectorCachedTileLoaderJob(
|
|---|
| 12713 | listener,
|
|---|
| 12714 | tile,
|
|---|
| 12715 | cache,
|
|---|
| 12716 | @@ -64,8 +64,8 @@ public class MapBoxVectorCachedTileLoader implements TileLoader, CachedTileLoade
|
|---|
| 12717 | @Override
|
|---|
| 12718 | public void cancelOutstandingTasks() {
|
|---|
| 12719 | final ThreadPoolExecutor executor = getDownloadExecutor();
|
|---|
| 12720 | - executor.getQueue().stream().filter(executor::remove).filter(MapBoxVectorCachedTileLoaderJob.class::isInstance)
|
|---|
| 12721 | - .map(MapBoxVectorCachedTileLoaderJob.class::cast).forEach(JCSCachedTileLoaderJob::handleJobCancellation);
|
|---|
| 12722 | + executor.getQueue().stream().filter(executor::remove).filter(MapboxVectorCachedTileLoaderJob.class::isInstance)
|
|---|
| 12723 | + .map(MapboxVectorCachedTileLoaderJob.class::cast).forEach(JCSCachedTileLoaderJob::handleJobCancellation);
|
|---|
| 12724 | }
|
|---|
| 12725 |
|
|---|
| 12726 | @Override
|
|---|
| 12727 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorCachedTileLoaderJob.java
|
|---|
| 12728 | similarity index 68%
|
|---|
| 12729 | rename from src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoaderJob.java
|
|---|
| 12730 | rename to src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorCachedTileLoaderJob.java
|
|---|
| 12731 | index 748172f5f..a6395cf61 100644
|
|---|
| 12732 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapBoxVectorCachedTileLoaderJob.java
|
|---|
| 12733 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorCachedTileLoaderJob.java
|
|---|
| 12734 | @@ -16,11 +16,11 @@ import org.apache.commons.jcs3.access.behavior.ICacheAccess;
|
|---|
| 12735 | * @author Taylor Smock
|
|---|
| 12736 | * @since xxx
|
|---|
| 12737 | */
|
|---|
| 12738 | -public class MapBoxVectorCachedTileLoaderJob extends TMSCachedTileLoaderJob {
|
|---|
| 12739 | +public class MapboxVectorCachedTileLoaderJob extends TMSCachedTileLoaderJob {
|
|---|
| 12740 |
|
|---|
| 12741 | - public MapBoxVectorCachedTileLoaderJob(TileLoaderListener listener, Tile tile,
|
|---|
| 12742 | - ICacheAccess<String, BufferedImageCacheEntry> cache, TileJobOptions options,
|
|---|
| 12743 | - ThreadPoolExecutor downloadExecutor) {
|
|---|
| 12744 | + public MapboxVectorCachedTileLoaderJob(TileLoaderListener listener, Tile tile,
|
|---|
| 12745 | + ICacheAccess<String, BufferedImageCacheEntry> cache, TileJobOptions options,
|
|---|
| 12746 | + ThreadPoolExecutor downloadExecutor) {
|
|---|
| 12747 | super(listener, tile, cache, options, downloadExecutor);
|
|---|
| 12748 | }
|
|---|
| 12749 | }
|
|---|
| 12750 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 12751 | index 413c7b32b..62647d1bb 100644
|
|---|
| 12752 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 12753 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 12754 | @@ -14,7 +14,7 @@ import javax.json.JsonReader;
|
|---|
| 12755 |
|
|---|
| 12756 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 12757 | import org.openstreetmap.josm.data.imagery.JosmTemplatedTMSTileSource;
|
|---|
| 12758 | -import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapBoxVectorStyle;
|
|---|
| 12759 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapboxVectorStyle;
|
|---|
| 12760 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.Source;
|
|---|
| 12761 | import org.openstreetmap.josm.gui.ExtendedDialog;
|
|---|
| 12762 | import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 12763 | @@ -29,7 +29,7 @@ import org.openstreetmap.josm.tools.Logging;
|
|---|
| 12764 | * @since xxx
|
|---|
| 12765 | */
|
|---|
| 12766 | public class MapboxVectorTileSource extends JosmTemplatedTMSTileSource {
|
|---|
| 12767 | - private final MapBoxVectorStyle styleSource;
|
|---|
| 12768 | + private final MapboxVectorStyle styleSource;
|
|---|
| 12769 |
|
|---|
| 12770 | /**
|
|---|
| 12771 | * Create a new {@link MapboxVectorTileSource} from an {@link ImageryInfo}
|
|---|
| 12772 | @@ -37,13 +37,13 @@ public class MapboxVectorTileSource extends JosmTemplatedTMSTileSource {
|
|---|
| 12773 | */
|
|---|
| 12774 | public MapboxVectorTileSource(ImageryInfo info) {
|
|---|
| 12775 | super(info);
|
|---|
| 12776 | - MapBoxVectorStyle mapBoxVectorStyle = null;
|
|---|
| 12777 | + MapboxVectorStyle mapBoxVectorStyle = null;
|
|---|
| 12778 | try (CachedFile style = new CachedFile(info.getUrl());
|
|---|
| 12779 | InputStream inputStream = style.getInputStream();
|
|---|
| 12780 | JsonReader reader = Json.createReader(inputStream)) {
|
|---|
| 12781 | reader.readObject();
|
|---|
| 12782 | // OK, we have a stylesheet
|
|---|
| 12783 | - mapBoxVectorStyle = MapBoxVectorStyle.getMapBoxVectorStyle(info.getUrl());
|
|---|
| 12784 | + mapBoxVectorStyle = MapboxVectorStyle.getMapboxVectorStyle(info.getUrl());
|
|---|
| 12785 | } catch (IOException | JsonException e) {
|
|---|
| 12786 | Logging.trace(e);
|
|---|
| 12787 | }
|
|---|
| 12788 | @@ -86,7 +86,7 @@ public class MapboxVectorTileSource extends JosmTemplatedTMSTileSource {
|
|---|
| 12789 | * Get the style source for this Vector Tile source
|
|---|
| 12790 | * @return The source to use for styling
|
|---|
| 12791 | */
|
|---|
| 12792 | - public MapBoxVectorStyle getStyleSource() {
|
|---|
| 12793 | + public MapboxVectorStyle getStyleSource() {
|
|---|
| 12794 | return this.styleSource;
|
|---|
| 12795 | }
|
|---|
| 12796 | }
|
|---|
| 12797 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 12798 | index 9488c3d19..d6e55972a 100644
|
|---|
| 12799 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 12800 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 12801 | @@ -21,7 +21,7 @@ import javax.json.JsonString;
|
|---|
| 12802 | import javax.json.JsonValue;
|
|---|
| 12803 |
|
|---|
| 12804 | /**
|
|---|
| 12805 | - * MapBox style layers
|
|---|
| 12806 | + * Mapbox style layers
|
|---|
| 12807 | * @author Taylor Smock
|
|---|
| 12808 | * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/">https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/</a>
|
|---|
| 12809 | * @since xxx
|
|---|
| 12810 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapboxVectorStyle.java
|
|---|
| 12811 | similarity index 95%
|
|---|
| 12812 | rename from src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 12813 | rename to src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapboxVectorStyle.java
|
|---|
| 12814 | index a945cbf57..68ca3dc52 100644
|
|---|
| 12815 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyle.java
|
|---|
| 12816 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapboxVectorStyle.java
|
|---|
| 12817 | @@ -47,20 +47,20 @@ import org.openstreetmap.josm.tools.Logging;
|
|---|
| 12818 | */
|
|---|
| 12819 | public class MapboxVectorStyle {
|
|---|
| 12820 |
|
|---|
| 12821 | - private static final ConcurrentHashMap<String, MapBoxVectorStyle> STYLE_MAPPING = new ConcurrentHashMap<>();
|
|---|
| 12822 | + private static final ConcurrentHashMap<String, MapboxVectorStyle> STYLE_MAPPING = new ConcurrentHashMap<>();
|
|---|
| 12823 |
|
|---|
| 12824 | /**
|
|---|
| 12825 | - * Get a MapBoxVector style for a URL
|
|---|
| 12826 | + * Get a MapboxVector style for a URL
|
|---|
| 12827 | * @param url The url to get
|
|---|
| 12828 | - * @return The MapBox Vector Style. May be {@code null} if there was an error.
|
|---|
| 12829 | + * @return The Mapbox Vector Style. May be {@code null} if there was an error.
|
|---|
| 12830 | */
|
|---|
| 12831 | - public static MapBoxVectorStyle getMapBoxVectorStyle(String url) {
|
|---|
| 12832 | + public static MapboxVectorStyle getMapboxVectorStyle(String url) {
|
|---|
| 12833 | return STYLE_MAPPING.computeIfAbsent(url, key -> {
|
|---|
| 12834 | try (CachedFile style = new CachedFile(url);
|
|---|
| 12835 | BufferedReader reader = style.getContentReader();
|
|---|
| 12836 | JsonReader jsonReader = Json.createReader(reader)) {
|
|---|
| 12837 | JsonStructure structure = jsonReader.read();
|
|---|
| 12838 | - return new MapBoxVectorStyle(structure.asJsonObject());
|
|---|
| 12839 | + return new MapboxVectorStyle(structure.asJsonObject());
|
|---|
| 12840 | } catch (IOException e) {
|
|---|
| 12841 | Logging.error(e);
|
|---|
| 12842 | }
|
|---|
| 12843 | @@ -82,13 +82,13 @@ public class MapboxVectorStyle {
|
|---|
| 12844 | private final Map<Source, ElemStyles> sources;
|
|---|
| 12845 |
|
|---|
| 12846 | /**
|
|---|
| 12847 | - * Create a new MapBoxVector style. You should prefer {@link #getMapBoxVectorStyle(String)}
|
|---|
| 12848 | + * Create a new MapboxVector style. You should prefer {@link #getMapboxVectorStyle(String)}
|
|---|
| 12849 | * for deduplication purposes.
|
|---|
| 12850 | *
|
|---|
| 12851 | * @param jsonObject The object to create the style from
|
|---|
| 12852 | - * @see #getMapBoxVectorStyle(String)
|
|---|
| 12853 | + * @see #getMapboxVectorStyle(String)
|
|---|
| 12854 | */
|
|---|
| 12855 | - public MapBoxVectorStyle(JsonObject jsonObject) {
|
|---|
| 12856 | + public MapboxVectorStyle(JsonObject jsonObject) {
|
|---|
| 12857 | // There should be a version specifier. We currently only support version 8.
|
|---|
| 12858 | // This can throw an NPE when there is no version number.
|
|---|
| 12859 | this.version = jsonObject.getInt("version");
|
|---|
| 12860 | @@ -261,7 +261,7 @@ public class MapboxVectorStyle {
|
|---|
| 12861 | @Override
|
|---|
| 12862 | public boolean equals(Object other) {
|
|---|
| 12863 | if (other != null && other.getClass() == this.getClass()) {
|
|---|
| 12864 | - MapBoxVectorStyle o = (MapBoxVectorStyle) other;
|
|---|
| 12865 | + MapboxVectorStyle o = (MapboxVectorStyle) other;
|
|---|
| 12866 | return this.version == o.version
|
|---|
| 12867 | && Objects.equals(this.name, o.name)
|
|---|
| 12868 | && Objects.equals(this.glyphUrl, o.glyphUrl)
|
|---|
| 12869 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 12870 | index dd41da72f..4e97e3c5d 100644
|
|---|
| 12871 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 12872 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Source.java
|
|---|
| 12873 | @@ -20,7 +20,7 @@ import org.openstreetmap.josm.data.Bounds;
|
|---|
| 12874 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.InvalidMapboxVectorTileException;
|
|---|
| 12875 |
|
|---|
| 12876 | /**
|
|---|
| 12877 | - * A source from a MapBox Vector Style
|
|---|
| 12878 | + * A source from a Mapbox Vector Style
|
|---|
| 12879 | *
|
|---|
| 12880 | * @author Taylor Smock
|
|---|
| 12881 | * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/">https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/</a>
|
|---|
| 12882 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java
|
|---|
| 12883 | index a086289d6..35a6114d2 100644
|
|---|
| 12884 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java
|
|---|
| 12885 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/SourceType.java
|
|---|
| 12886 | @@ -2,7 +2,7 @@
|
|---|
| 12887 | package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 12888 |
|
|---|
| 12889 | /**
|
|---|
| 12890 | - * The "source type" for the data (MapBox Vector Style specification)
|
|---|
| 12891 | + * The "source type" for the data (Mapbox Vector Style specification)
|
|---|
| 12892 | *
|
|---|
| 12893 | * @author Taylor Smock
|
|---|
| 12894 | * @since xxx
|
|---|
| 12895 | diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 12896 | index 2e7aac3e6..6079b24ce 100644
|
|---|
| 12897 | --- a/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 12898 | +++ b/src/org/openstreetmap/josm/gui/layer/imagery/MVTLayer.java
|
|---|
| 12899 | @@ -32,7 +32,7 @@ import org.openstreetmap.josm.data.imagery.vectortile.mapbox.Layer;
|
|---|
| 12900 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTFile;
|
|---|
| 12901 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 12902 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile.TileListener;
|
|---|
| 12903 | -import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapBoxVectorCachedTileLoader;
|
|---|
| 12904 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorCachedTileLoader;
|
|---|
| 12905 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorTileSource;
|
|---|
| 12906 | import org.openstreetmap.josm.data.osm.DataSet;
|
|---|
| 12907 | import org.openstreetmap.josm.data.osm.Node;
|
|---|
| 12908 | @@ -57,7 +57,7 @@ import org.openstreetmap.josm.gui.mappaint.ElemStyles;
|
|---|
| 12909 | import org.openstreetmap.josm.gui.mappaint.StyleSource;
|
|---|
| 12910 |
|
|---|
| 12911 | /**
|
|---|
| 12912 | - * A layer for MapBox Vector Tiles
|
|---|
| 12913 | + * A layer for Mapbox Vector Tiles
|
|---|
| 12914 | * @author Taylor Smock
|
|---|
| 12915 | * @since xxx
|
|---|
| 12916 | */
|
|---|
| 12917 | @@ -79,7 +79,7 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 12918 |
|
|---|
| 12919 | @Override
|
|---|
| 12920 | protected Class<? extends TileLoader> getTileLoaderClass() {
|
|---|
| 12921 | - return MapBoxVectorCachedTileLoader.class;
|
|---|
| 12922 | + return MapboxVectorCachedTileLoader.class;
|
|---|
| 12923 | }
|
|---|
| 12924 |
|
|---|
| 12925 | @Override
|
|---|
| 12926 | @@ -89,7 +89,7 @@ public class MVTLayer extends AbstractCachedTileSourceLayer<MapboxVectorTileSour
|
|---|
| 12927 |
|
|---|
| 12928 | @Override
|
|---|
| 12929 | public Collection<String> getNativeProjections() {
|
|---|
| 12930 | - // MapBox Vector Tiles <i>specifically</i> only support EPSG:3857
|
|---|
| 12931 | + // Mapbox Vector Tiles <i>specifically</i> only support EPSG:3857
|
|---|
| 12932 | // ("it is exclusively geared towards square pixel tiles in {link to EPSG:3857}").
|
|---|
| 12933 | return Collections.singleton(MVTFile.DEFAULT_PROJECTION);
|
|---|
| 12934 | }
|
|---|
| 12935 | diff --git a/src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java b/src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java
|
|---|
| 12936 | index 99bbd058d..b1f7a1653 100644
|
|---|
| 12937 | --- a/src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java
|
|---|
| 12938 | +++ b/src/org/openstreetmap/josm/gui/preferences/imagery/AddMVTLayerPanel.java
|
|---|
| 12939 | @@ -17,7 +17,7 @@ import org.openstreetmap.josm.tools.GBC;
|
|---|
| 12940 | import org.openstreetmap.josm.tools.Utils;
|
|---|
| 12941 |
|
|---|
| 12942 | /**
|
|---|
| 12943 | - * A panel for adding MapBox Vector Tile layers
|
|---|
| 12944 | + * A panel for adding Mapbox Vector Tile layers
|
|---|
| 12945 | * @author Taylor Smock
|
|---|
| 12946 | * @since xxx
|
|---|
| 12947 | */
|
|---|
| 12948 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 12949 | index 12b86ebc7..f76f016cf 100644
|
|---|
| 12950 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 12951 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTileTest.java
|
|---|
| 12952 | @@ -29,14 +29,14 @@ import org.junit.jupiter.params.provider.MethodSource;
|
|---|
| 12953 | */
|
|---|
| 12954 | public class MVTTileTest {
|
|---|
| 12955 | private MapboxVectorTileSource tileSource;
|
|---|
| 12956 | - private MapBoxVectorCachedTileLoader loader;
|
|---|
| 12957 | + private MapboxVectorCachedTileLoader loader;
|
|---|
| 12958 | @RegisterExtension
|
|---|
| 12959 | JOSMTestRules rule = new JOSMTestRules();
|
|---|
| 12960 | @BeforeEach
|
|---|
| 12961 | void setup() {
|
|---|
| 12962 | tileSource = new MapboxVectorTileSource(new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot()
|
|---|
| 12963 | + "pbf/mapillary/{z}/{x}/{y}.mvt"));
|
|---|
| 12964 | - loader = new MapBoxVectorCachedTileLoader(null,
|
|---|
| 12965 | + loader = new MapboxVectorCachedTileLoader(null,
|
|---|
| 12966 | JCSCacheManager.getCache("testMapillaryCache"), new TileJobOptions(1, 1, Collections
|
|---|
| 12967 | .emptyMap(), 3600));
|
|---|
| 12968 | }
|
|---|
| 12969 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java
|
|---|
| 12970 | index 5b9f16842..95a9874fe 100644
|
|---|
| 12971 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java
|
|---|
| 12972 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java
|
|---|
| 12973 | @@ -11,7 +11,7 @@ import java.util.stream.Stream;
|
|---|
| 12974 | import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 12975 | import org.openstreetmap.josm.TestUtils;
|
|---|
| 12976 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 12977 | -import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapBoxVectorStyle;
|
|---|
| 12978 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapboxVectorStyle;
|
|---|
| 12979 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.Source;
|
|---|
| 12980 | import org.openstreetmap.josm.gui.ExtendedDialog;
|
|---|
| 12981 | import org.openstreetmap.josm.gui.widgets.JosmComboBox;
|
|---|
| 12982 | @@ -70,7 +70,7 @@ class MapboxVectorTileSourceTest {
|
|---|
| 12983 | extendedDialogMocker.getMockResultMap().put(dialogMockerText, "Add layers");
|
|---|
| 12984 | MapboxVectorTileSource tileSource = new MapboxVectorTileSource(
|
|---|
| 12985 | new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot() + "mapillary.json"));
|
|---|
| 12986 | - MapBoxVectorStyle styleSource = tileSource.getStyleSource();
|
|---|
| 12987 | + MapboxVectorStyle styleSource = tileSource.getStyleSource();
|
|---|
| 12988 | assertNotNull(styleSource);
|
|---|
| 12989 | assertEquals(expected, tileSource.toString());
|
|---|
| 12990 | }
|
|---|
| 12991 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyleTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapboxVectorStyleTest.java
|
|---|
| 12992 | similarity index 95%
|
|---|
| 12993 | rename from test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyleTest.java
|
|---|
| 12994 | rename to test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapboxVectorStyleTest.java
|
|---|
| 12995 | index 1fcb7bfe8..461feffa0 100644
|
|---|
| 12996 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapBoxVectorStyleTest.java
|
|---|
| 12997 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/MapboxVectorStyleTest.java
|
|---|
| 12998 | @@ -56,10 +56,10 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 12999 | import org.junit.jupiter.api.io.TempDir;
|
|---|
| 13000 |
|
|---|
| 13001 | /**
|
|---|
| 13002 | - * Test class for {@link MapBoxVectorStyle}
|
|---|
| 13003 | + * Test class for {@link MapboxVectorStyle}
|
|---|
| 13004 | * @author Taylor Smock
|
|---|
| 13005 | */
|
|---|
| 13006 | -public class MapBoxVectorStyleTest {
|
|---|
| 13007 | +public class MapboxVectorStyleTest {
|
|---|
| 13008 | /** Used to store sprite files (specifically, sprite{,@2x}.{png,json}) */
|
|---|
| 13009 | @TempDir
|
|---|
| 13010 | File spritesDirectory;
|
|---|
| 13011 | @@ -86,21 +86,21 @@ public class MapBoxVectorStyleTest {
|
|---|
| 13012 | */
|
|---|
| 13013 | @Test
|
|---|
| 13014 | void testVersionChecks() {
|
|---|
| 13015 | - assertThrows(NullPointerException.class, () -> new MapBoxVectorStyle(JsonValue.EMPTY_JSON_OBJECT));
|
|---|
| 13016 | + assertThrows(NullPointerException.class, () -> new MapboxVectorStyle(JsonValue.EMPTY_JSON_OBJECT));
|
|---|
| 13017 | IllegalArgumentException badVersion = assertThrows(IllegalArgumentException.class,
|
|---|
| 13018 | - () -> new MapBoxVectorStyle(Json.createObjectBuilder().add("version", 7).build()));
|
|---|
| 13019 | + () -> new MapboxVectorStyle(Json.createObjectBuilder().add("version", 7).build()));
|
|---|
| 13020 | assertEquals("Vector Tile Style Version not understood: version 7 (json: {\"version\":7})", badVersion.getMessage());
|
|---|
| 13021 | badVersion = assertThrows(IllegalArgumentException.class,
|
|---|
| 13022 | - () -> new MapBoxVectorStyle(Json.createObjectBuilder().add("version", 9).build()));
|
|---|
| 13023 | + () -> new MapboxVectorStyle(Json.createObjectBuilder().add("version", 9).build()));
|
|---|
| 13024 | assertEquals("Vector Tile Style Version not understood: version 9 (json: {\"version\":9})", badVersion.getMessage());
|
|---|
| 13025 | - assertDoesNotThrow(() -> new MapBoxVectorStyle(Json.createObjectBuilder().add("version", 8).build()));
|
|---|
| 13026 | + assertDoesNotThrow(() -> new MapboxVectorStyle(Json.createObjectBuilder().add("version", 8).build()));
|
|---|
| 13027 | }
|
|---|
| 13028 |
|
|---|
| 13029 | @Test
|
|---|
| 13030 | void testSources() {
|
|---|
| 13031 | // Check with an invalid sources list
|
|---|
| 13032 | - assertTrue(new MapBoxVectorStyle(getJson(JsonObject.class, "{\"version\":8,\"sources\":[\"s1\",\"s2\"]}")).getSources().isEmpty());
|
|---|
| 13033 | - Map<Source, ElemStyles> sources = new MapBoxVectorStyle(getJson(JsonObject.class, MessageFormat.format(BASE_STYLE, "test",
|
|---|
| 13034 | + assertTrue(new MapboxVectorStyle(getJson(JsonObject.class, "{\"version\":8,\"sources\":[\"s1\",\"s2\"]}")).getSources().isEmpty());
|
|---|
| 13035 | + Map<Source, ElemStyles> sources = new MapboxVectorStyle(getJson(JsonObject.class, MessageFormat.format(BASE_STYLE, "test",
|
|---|
| 13036 | MessageFormat.format("\"sources\":'{'{0},{1},\"source3\":[\"bad source\"]'}',\"layers\":[{2},{3},{4}]",
|
|---|
| 13037 | SOURCE1, SOURCE2, LAYER1, LAYER2, LAYER2.replace('2', '3'))))).getSources();
|
|---|
| 13038 | assertEquals(3, sources.size());
|
|---|
| 13039 | @@ -113,8 +113,8 @@ public class MapBoxVectorStyleTest {
|
|---|
| 13040 |
|
|---|
| 13041 | @Test
|
|---|
| 13042 | void testSavedFiles() {
|
|---|
| 13043 | - assertTrue(new MapBoxVectorStyle(getJson(JsonObject.class, "{\"version\":8,\"sources\":[\"s1\",\"s2\"]}")).getSources().isEmpty());
|
|---|
| 13044 | - Map<Source, ElemStyles> sources = new MapBoxVectorStyle(getJson(JsonObject.class, MessageFormat.format(BASE_STYLE, "test",
|
|---|
| 13045 | + assertTrue(new MapboxVectorStyle(getJson(JsonObject.class, "{\"version\":8,\"sources\":[\"s1\",\"s2\"]}")).getSources().isEmpty());
|
|---|
| 13046 | + Map<Source, ElemStyles> sources = new MapboxVectorStyle(getJson(JsonObject.class, MessageFormat.format(BASE_STYLE, "test",
|
|---|
| 13047 | MessageFormat.format("\"sources\":'{'{0},{1}'}',\"layers\":[{2},{3}]", SOURCE1, SOURCE2, LAYER1, LAYER2)))).getSources();
|
|---|
| 13048 | assertEquals(2, sources.size());
|
|---|
| 13049 | // For various reasons, the map _must_ be reliably ordered in the order of encounter
|
|---|
| 13050 | @@ -162,7 +162,7 @@ public class MapBoxVectorStyleTest {
|
|---|
| 13051 | ImageProvider.clearCache();
|
|---|
| 13052 | int hiDpiScalar = hiDpi ? 2 : 1;
|
|---|
| 13053 | String spritePath = new File(this.spritesDirectory, "sprite").getPath();
|
|---|
| 13054 | - MapBoxVectorStyle style = new MapBoxVectorStyle(getJson(JsonObject.class,
|
|---|
| 13055 | + MapboxVectorStyle style = new MapboxVectorStyle(getJson(JsonObject.class,
|
|---|
| 13056 | MessageFormat.format(BASE_STYLE, "sprite_test", "\"sprite\":\"file:/" + spritePath + "\"")));
|
|---|
| 13057 | assertEquals("file:/" + spritePath, style.getSpriteUrl());
|
|---|
| 13058 |
|
|---|
| 13059 | @@ -238,7 +238,7 @@ public class MapBoxVectorStyleTest {
|
|---|
| 13060 | @Test
|
|---|
| 13061 | void testMapillaryStyle() {
|
|---|
| 13062 | final String file = Paths.get("file:", TestUtils.getTestDataRoot(), "mapillary.json").toString();
|
|---|
| 13063 | - final MapBoxVectorStyle style = MapBoxVectorStyle.getMapBoxVectorStyle(file);
|
|---|
| 13064 | + final MapboxVectorStyle style = MapboxVectorStyle.getMapboxVectorStyle(file);
|
|---|
| 13065 | assertNotNull(style);
|
|---|
| 13066 | // There are three "sources" in the mapillary.json file
|
|---|
| 13067 | assertEquals(3, style.getSources().size());
|
|---|
| 13068 | @@ -266,7 +266,7 @@ public class MapBoxVectorStyleTest {
|
|---|
| 13069 | StyleSource node = new MapCSSStyleSource("meta{title:\"node\";}node{text:ref;}");
|
|---|
| 13070 | node.loadStyleSource();
|
|---|
| 13071 | canvas.loadStyleSource();
|
|---|
| 13072 | - EqualsVerifier.forClass(MapBoxVectorStyle.class)
|
|---|
| 13073 | + EqualsVerifier.forClass(MapboxVectorStyle.class)
|
|---|
| 13074 | .withPrefabValues(ImageProvider.class, new ImageProvider("cancel"), new ImageProvider("ok"))
|
|---|
| 13075 | .withPrefabValues(StyleSource.class, canvas, node)
|
|---|
| 13076 | .usingGetClass().verify();
|
|---|
| 13077 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 13078 | index 35e208979..0e7a572d5 100644
|
|---|
| 13079 | --- a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 13080 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 13081 | @@ -18,7 +18,7 @@ import java.util.stream.Collectors;
|
|---|
| 13082 | import org.openstreetmap.josm.TestUtils;
|
|---|
| 13083 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 13084 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 13085 | -import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapBoxVectorCachedTileLoader;
|
|---|
| 13086 | +import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorCachedTileLoader;
|
|---|
| 13087 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorTileSource;
|
|---|
| 13088 | import org.openstreetmap.josm.gui.layer.imagery.MVTLayer;
|
|---|
| 13089 | import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 13090 | @@ -48,12 +48,12 @@ class VectorDataSetTest {
|
|---|
| 13091 | return super.getTileSource();
|
|---|
| 13092 | }
|
|---|
| 13093 |
|
|---|
| 13094 | - protected MapBoxVectorCachedTileLoader getTileLoader() {
|
|---|
| 13095 | + protected MapboxVectorCachedTileLoader getTileLoader() {
|
|---|
| 13096 | if (this.tileLoader == null) {
|
|---|
| 13097 | this.tileLoader = this.getTileLoaderFactory().makeTileLoader(this, Collections.emptyMap(), 7200);
|
|---|
| 13098 | }
|
|---|
| 13099 | - if (this.tileLoader instanceof MapBoxVectorCachedTileLoader) {
|
|---|
| 13100 | - return (MapBoxVectorCachedTileLoader) this.tileLoader;
|
|---|
| 13101 | + if (this.tileLoader instanceof MapboxVectorCachedTileLoader) {
|
|---|
| 13102 | + return (MapboxVectorCachedTileLoader) this.tileLoader;
|
|---|
| 13103 | }
|
|---|
| 13104 | return null;
|
|---|
| 13105 | }
|
|---|
| 13106 | @@ -82,7 +82,7 @@ class VectorDataSetTest {
|
|---|
| 13107 | throw new IllegalArgumentException("Tiles come with a {z}, {x}, and {y} component");
|
|---|
| 13108 | }
|
|---|
| 13109 | final MapboxVectorTileSource tileSource = layer.getTileSource();
|
|---|
| 13110 | - MapBoxVectorCachedTileLoader tileLoader = layer.getTileLoader();
|
|---|
| 13111 | + MapboxVectorCachedTileLoader tileLoader = layer.getTileLoader();
|
|---|
| 13112 | Collection<MVTTile> tilesCollection = new ArrayList<>();
|
|---|
| 13113 | for (int i = 0; i < tiles.length / 3; i++) {
|
|---|
| 13114 | final MVTTile tile = (MVTTile) layer.createTile(tileSource, tiles[3 * i + 1], tiles[3 * i + 2], tiles[3 * i]);
|
|---|
| 13115 | --
|
|---|
| 13116 | GitLab
|
|---|
| 13117 |
|
|---|
| 13118 |
|
|---|
| 13119 | From bca406ffcbebc18ee6bc9e5fd25a720aa6e8a799 Mon Sep 17 00:00:00 2001
|
|---|
| 13120 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 13121 | Date: Mon, 3 May 2021 07:19:34 -0600
|
|---|
| 13122 | Subject: [PATCH 30/50] JCSCachedTileLoader: Use Utils.readBytesFromStream
|
|---|
| 13123 | (patch by simon04)
|
|---|
| 13124 |
|
|---|
| 13125 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 13126 | ---
|
|---|
| 13127 | .../openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java | 2 +-
|
|---|
| 13128 | 1 file changed, 1 insertion(+), 1 deletion(-)
|
|---|
| 13129 |
|
|---|
| 13130 | diff --git a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 13131 | index eeac761c6..d07dff6d1 100644
|
|---|
| 13132 | --- a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 13133 | +++ b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 13134 | @@ -321,7 +321,7 @@ public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements
|
|---|
| 13135 | file = new File(fileName.substring("file://".length() - 1));
|
|---|
| 13136 | }
|
|---|
| 13137 | try (InputStream fileInputStream = Files.newInputStream(file.toPath())) {
|
|---|
| 13138 | - cacheData = createCacheEntry(IOUtils.toByteArray(fileInputStream));
|
|---|
| 13139 | + cacheData = createCacheEntry(Utils.readBytesFromStream(fileInputStream));
|
|---|
| 13140 | cache.put(getCacheKey(), cacheData, attributes);
|
|---|
| 13141 | return true;
|
|---|
| 13142 | } catch (IOException e) {
|
|---|
| 13143 | --
|
|---|
| 13144 | GitLab
|
|---|
| 13145 |
|
|---|
| 13146 |
|
|---|
| 13147 | From 1ff96e03474a501025e871db61c5eb72bbf65c8f Mon Sep 17 00:00:00 2001
|
|---|
| 13148 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 13149 | Date: Mon, 3 May 2021 07:28:12 -0600
|
|---|
| 13150 | Subject: [PATCH 31/50] WaySegment: Add methods for binary compatibility
|
|---|
| 13151 |
|
|---|
| 13152 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 13153 | ---
|
|---|
| 13154 | .../josm/data/osm/WaySegment.java | 50 ++++++++++++++++++-
|
|---|
| 13155 | 1 file changed, 48 insertions(+), 2 deletions(-)
|
|---|
| 13156 |
|
|---|
| 13157 | diff --git a/src/org/openstreetmap/josm/data/osm/WaySegment.java b/src/org/openstreetmap/josm/data/osm/WaySegment.java
|
|---|
| 13158 | index 302f82842..a419980b5 100644
|
|---|
| 13159 | --- a/src/org/openstreetmap/josm/data/osm/WaySegment.java
|
|---|
| 13160 | +++ b/src/org/openstreetmap/josm/data/osm/WaySegment.java
|
|---|
| 13161 | @@ -21,8 +21,8 @@ public final class WaySegment extends IWaySegment<Node, Way> {
|
|---|
| 13162 | * Determines and returns the way segment for the given way and node pair. You should prefer
|
|---|
| 13163 | * {@link IWaySegment#forNodePair(IWay, INode, INode)} whenever possible.
|
|---|
| 13164 | *
|
|---|
| 13165 | - * @param way way
|
|---|
| 13166 | - * @param first first node
|
|---|
| 13167 | + * @param way way
|
|---|
| 13168 | + * @param first first node
|
|---|
| 13169 | * @param second second node
|
|---|
| 13170 | * @return way segment
|
|---|
| 13171 | * @throws IllegalArgumentException if the node pair is not part of way
|
|---|
| 13172 | @@ -39,6 +39,19 @@ public final class WaySegment extends IWaySegment<Node, Way> {
|
|---|
| 13173 | throw new IllegalArgumentException("Node pair is not part of way!");
|
|---|
| 13174 | }
|
|---|
| 13175 |
|
|---|
| 13176 | + @Override
|
|---|
| 13177 | + public Node getFirstNode() {
|
|---|
| 13178 | + // This is kept for binary compatibility
|
|---|
| 13179 | + return super.getFirstNode();
|
|---|
| 13180 | + }
|
|---|
| 13181 | +
|
|---|
| 13182 | + @Override
|
|---|
| 13183 | + public Node getSecondNode() {
|
|---|
| 13184 | + // This is kept for binary compatibility
|
|---|
| 13185 | + return super.getSecondNode();
|
|---|
| 13186 | + }
|
|---|
| 13187 | +
|
|---|
| 13188 | +
|
|---|
| 13189 | /**
|
|---|
| 13190 | * Returns this way segment as complete way.
|
|---|
| 13191 | * @return the way segment as {@code Way}
|
|---|
| 13192 | @@ -51,6 +64,39 @@ public final class WaySegment extends IWaySegment<Node, Way> {
|
|---|
| 13193 | return w;
|
|---|
| 13194 | }
|
|---|
| 13195 |
|
|---|
| 13196 | + @Override
|
|---|
| 13197 | + public boolean equals(Object o) {
|
|---|
| 13198 | + // This is kept for binary compatibility
|
|---|
| 13199 | + return super.equals(o);
|
|---|
| 13200 | + }
|
|---|
| 13201 | +
|
|---|
| 13202 | + @Override
|
|---|
| 13203 | + public int hashCode() {
|
|---|
| 13204 | + // This is kept for binary compatibility
|
|---|
| 13205 | + return super.hashCode();
|
|---|
| 13206 | + }
|
|---|
| 13207 | +
|
|---|
| 13208 | + /**
|
|---|
| 13209 | + * Checks whether this segment crosses other segment
|
|---|
| 13210 | + *
|
|---|
| 13211 | + * @param s2 The other segment
|
|---|
| 13212 | + * @return true if both segments crosses
|
|---|
| 13213 | + */
|
|---|
| 13214 | + public boolean intersects(WaySegment s2) {
|
|---|
| 13215 | + // This is kept for binary compatibility
|
|---|
| 13216 | + return super.intersects(s2);
|
|---|
| 13217 | + }
|
|---|
| 13218 | +
|
|---|
| 13219 | + /**
|
|---|
| 13220 | + * Checks whether this segment and another way segment share the same points
|
|---|
| 13221 | + * @param s2 The other segment
|
|---|
| 13222 | + * @return true if other way segment is the same or reverse
|
|---|
| 13223 | + */
|
|---|
| 13224 | + public boolean isSimilar(WaySegment s2) {
|
|---|
| 13225 | + // This is kept for binary compatibility
|
|---|
| 13226 | + return super.isSimilar(s2);
|
|---|
| 13227 | + }
|
|---|
| 13228 | +
|
|---|
| 13229 | @Override
|
|---|
| 13230 | public String toString() {
|
|---|
| 13231 | return "WaySegment [way=" + way.getUniqueId() + ", lowerIndex=" + lowerIndex + ']';
|
|---|
| 13232 | --
|
|---|
| 13233 | GitLab
|
|---|
| 13234 |
|
|---|
| 13235 |
|
|---|
| 13236 | From b24efb00bb6569ba63214ea73ae843248d582ec9 Mon Sep 17 00:00:00 2001
|
|---|
| 13237 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 13238 | Date: Mon, 3 May 2021 07:31:28 -0600
|
|---|
| 13239 | Subject: [PATCH 32/50] ProtoBuf -> Protobuf
|
|---|
| 13240 |
|
|---|
| 13241 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 13242 | ---
|
|---|
| 13243 | .../imagery/vectortile/mapbox/Feature.java | 18 +++----
|
|---|
| 13244 | .../data/imagery/vectortile/mapbox/Layer.java | 48 ++++++++---------
|
|---|
| 13245 | .../imagery/vectortile/mapbox/MVTTile.java | 12 ++---
|
|---|
| 13246 | ...rotoBufPacked.java => ProtobufPacked.java} | 14 ++---
|
|---|
| 13247 | ...rotoBufParser.java => ProtobufParser.java} | 12 ++---
|
|---|
| 13248 | ...rotoBufRecord.java => ProtobufRecord.java} | 14 ++---
|
|---|
| 13249 | .../imagery/vectortile/mapbox/LayerTest.java | 12 ++---
|
|---|
| 13250 | .../data/protobuf/ProtoBufParserTest.java | 51 -------------------
|
|---|
| 13251 | .../josm/data/protobuf/ProtoBufTest.java | 36 ++++++-------
|
|---|
| 13252 | .../data/protobuf/ProtobufParserTest.java | 51 +++++++++++++++++++
|
|---|
| 13253 | ...ecordTest.java => ProtobufRecordTest.java} | 12 ++---
|
|---|
| 13254 | 11 files changed, 140 insertions(+), 140 deletions(-)
|
|---|
| 13255 | rename src/org/openstreetmap/josm/data/protobuf/{ProtoBufPacked.java => ProtobufPacked.java} (79%)
|
|---|
| 13256 | rename src/org/openstreetmap/josm/data/protobuf/{ProtoBufParser.java => ProtobufParser.java} (95%)
|
|---|
| 13257 | rename src/org/openstreetmap/josm/data/protobuf/{ProtoBufRecord.java => ProtobufRecord.java} (86%)
|
|---|
| 13258 | delete mode 100644 test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java
|
|---|
| 13259 | create mode 100644 test/unit/org/openstreetmap/josm/data/protobuf/ProtobufParserTest.java
|
|---|
| 13260 | rename test/unit/org/openstreetmap/josm/data/protobuf/{ProtoBufRecordTest.java => ProtobufRecordTest.java} (67%)
|
|---|
| 13261 |
|
|---|
| 13262 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
|
|---|
| 13263 | index df194cc00..f0778b250 100644
|
|---|
| 13264 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
|
|---|
| 13265 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
|
|---|
| 13266 | @@ -7,9 +7,9 @@ import java.util.ArrayList;
|
|---|
| 13267 | import java.util.List;
|
|---|
| 13268 |
|
|---|
| 13269 | import org.openstreetmap.josm.data.osm.TagMap;
|
|---|
| 13270 | -import org.openstreetmap.josm.data.protobuf.ProtoBufPacked;
|
|---|
| 13271 | -import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 13272 | -import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 13273 | +import org.openstreetmap.josm.data.protobuf.ProtobufPacked;
|
|---|
| 13274 | +import org.openstreetmap.josm.data.protobuf.ProtobufParser;
|
|---|
| 13275 | +import org.openstreetmap.josm.data.protobuf.ProtobufRecord;
|
|---|
| 13276 | import org.openstreetmap.josm.tools.Utils;
|
|---|
| 13277 |
|
|---|
| 13278 | /**
|
|---|
| 13279 | @@ -50,25 +50,25 @@ public class Feature {
|
|---|
| 13280 | * @param record The record to create the feature from
|
|---|
| 13281 | * @throws IOException - if an IO error occurs
|
|---|
| 13282 | */
|
|---|
| 13283 | - public Feature(Layer layer, ProtoBufRecord record) throws IOException {
|
|---|
| 13284 | + public Feature(Layer layer, ProtobufRecord record) throws IOException {
|
|---|
| 13285 | long tId = 0;
|
|---|
| 13286 | GeometryTypes geometryTypeTemp = GeometryTypes.UNKNOWN;
|
|---|
| 13287 | String key = null;
|
|---|
| 13288 | - try (ProtoBufParser parser = new ProtoBufParser(record.getBytes())) {
|
|---|
| 13289 | + try (ProtobufParser parser = new ProtobufParser(record.getBytes())) {
|
|---|
| 13290 | while (parser.hasNext()) {
|
|---|
| 13291 | - try (ProtoBufRecord next = new ProtoBufRecord(parser)) {
|
|---|
| 13292 | + try (ProtobufRecord next = new ProtobufRecord(parser)) {
|
|---|
| 13293 | if (next.getField() == TAG_FIELD) {
|
|---|
| 13294 | if (tags == null) {
|
|---|
| 13295 | tags = new TagMap();
|
|---|
| 13296 | }
|
|---|
| 13297 | // This is packed in v1 and v2
|
|---|
| 13298 | - ProtoBufPacked packed = new ProtoBufPacked(next.getBytes());
|
|---|
| 13299 | + ProtobufPacked packed = new ProtobufPacked(next.getBytes());
|
|---|
| 13300 | for (Number number : packed.getArray()) {
|
|---|
| 13301 | key = parseTagValue(key, layer, number);
|
|---|
| 13302 | }
|
|---|
| 13303 | } else if (next.getField() == GEOMETRY_FIELD) {
|
|---|
| 13304 | // This is packed in v1 and v2
|
|---|
| 13305 | - ProtoBufPacked packed = new ProtoBufPacked(next.getBytes());
|
|---|
| 13306 | + ProtobufPacked packed = new ProtobufPacked(next.getBytes());
|
|---|
| 13307 | CommandInteger currentCommand = null;
|
|---|
| 13308 | for (Number number : packed.getArray()) {
|
|---|
| 13309 | if (currentCommand != null && currentCommand.hasAllExpectedParameters()) {
|
|---|
| 13310 | @@ -78,7 +78,7 @@ public class Feature {
|
|---|
| 13311 | currentCommand = new CommandInteger(number.intValue());
|
|---|
| 13312 | this.geometry.add(currentCommand);
|
|---|
| 13313 | } else {
|
|---|
| 13314 | - currentCommand.addParameter(ProtoBufParser.decodeZigZag(number));
|
|---|
| 13315 | + currentCommand.addParameter(ProtobufParser.decodeZigZag(number));
|
|---|
| 13316 | }
|
|---|
| 13317 | }
|
|---|
| 13318 | // TODO fallback to non-packed
|
|---|
| 13319 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 13320 | index b0b344da1..ed36061d1 100644
|
|---|
| 13321 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 13322 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Layer.java
|
|---|
| 13323 | @@ -14,8 +14,8 @@ import java.util.Objects;
|
|---|
| 13324 | import java.util.function.Function;
|
|---|
| 13325 | import java.util.stream.Collectors;
|
|---|
| 13326 |
|
|---|
| 13327 | -import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 13328 | -import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 13329 | +import org.openstreetmap.josm.data.protobuf.ProtobufParser;
|
|---|
| 13330 | +import org.openstreetmap.josm.data.protobuf.ProtobufRecord;
|
|---|
| 13331 | import org.openstreetmap.josm.tools.Destroyable;
|
|---|
| 13332 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 13333 |
|
|---|
| 13334 | @@ -26,13 +26,13 @@ import org.openstreetmap.josm.tools.Logging;
|
|---|
| 13335 | */
|
|---|
| 13336 | public final class Layer implements Destroyable {
|
|---|
| 13337 | private static final class ValueFields<T> {
|
|---|
| 13338 | - static final ValueFields<String> STRING = new ValueFields<>(1, ProtoBufRecord::asString);
|
|---|
| 13339 | - static final ValueFields<Float> FLOAT = new ValueFields<>(2, ProtoBufRecord::asFloat);
|
|---|
| 13340 | - static final ValueFields<Double> DOUBLE = new ValueFields<>(3, ProtoBufRecord::asDouble);
|
|---|
| 13341 | - static final ValueFields<Number> INT64 = new ValueFields<>(4, ProtoBufRecord::asUnsignedVarInt);
|
|---|
| 13342 | + static final ValueFields<String> STRING = new ValueFields<>(1, ProtobufRecord::asString);
|
|---|
| 13343 | + static final ValueFields<Float> FLOAT = new ValueFields<>(2, ProtobufRecord::asFloat);
|
|---|
| 13344 | + static final ValueFields<Double> DOUBLE = new ValueFields<>(3, ProtobufRecord::asDouble);
|
|---|
| 13345 | + static final ValueFields<Number> INT64 = new ValueFields<>(4, ProtobufRecord::asUnsignedVarInt);
|
|---|
| 13346 | // This may have issues if there are actual uint_values (i.e., more than {@link Long#MAX_VALUE})
|
|---|
| 13347 | - static final ValueFields<Number> UINT64 = new ValueFields<>(5, ProtoBufRecord::asUnsignedVarInt);
|
|---|
| 13348 | - static final ValueFields<Number> SINT64 = new ValueFields<>(6, ProtoBufRecord::asSignedVarInt);
|
|---|
| 13349 | + static final ValueFields<Number> UINT64 = new ValueFields<>(5, ProtobufRecord::asUnsignedVarInt);
|
|---|
| 13350 | + static final ValueFields<Number> SINT64 = new ValueFields<>(6, ProtobufRecord::asSignedVarInt);
|
|---|
| 13351 | static final ValueFields<Boolean> BOOL = new ValueFields<>(7, r -> r.asUnsignedVarInt().longValue() != 0);
|
|---|
| 13352 |
|
|---|
| 13353 | /**
|
|---|
| 13354 | @@ -42,8 +42,8 @@ public final class Layer implements Destroyable {
|
|---|
| 13355 | Collections.unmodifiableList(Arrays.asList(STRING, FLOAT, DOUBLE, INT64, UINT64, SINT64, BOOL));
|
|---|
| 13356 |
|
|---|
| 13357 | private final byte field;
|
|---|
| 13358 | - private final Function<ProtoBufRecord, T> conversion;
|
|---|
| 13359 | - private ValueFields(int field, Function<ProtoBufRecord, T> conversion) {
|
|---|
| 13360 | + private final Function<ProtobufRecord, T> conversion;
|
|---|
| 13361 | + private ValueFields(int field, Function<ProtobufRecord, T> conversion) {
|
|---|
| 13362 | this.field = (byte) field;
|
|---|
| 13363 | this.conversion = conversion;
|
|---|
| 13364 | }
|
|---|
| 13365 | @@ -61,12 +61,12 @@ public final class Layer implements Destroyable {
|
|---|
| 13366 | * @param protobufRecord The record to convert
|
|---|
| 13367 | * @return the converted value
|
|---|
| 13368 | */
|
|---|
| 13369 | - public T convertValue(ProtoBufRecord protobufRecord) {
|
|---|
| 13370 | + public T convertValue(ProtobufRecord protobufRecord) {
|
|---|
| 13371 | return this.conversion.apply(protobufRecord);
|
|---|
| 13372 | }
|
|---|
| 13373 | }
|
|---|
| 13374 |
|
|---|
| 13375 | - /** The field value for a layer (in {@link ProtoBufRecord#getField}) */
|
|---|
| 13376 | + /** The field value for a layer (in {@link ProtobufRecord#getField}) */
|
|---|
| 13377 | public static final byte LAYER_FIELD = 3;
|
|---|
| 13378 | private static final byte VERSION_FIELD = 15;
|
|---|
| 13379 | private static final byte NAME_FIELD = 1;
|
|---|
| 13380 | @@ -97,26 +97,26 @@ public final class Layer implements Destroyable {
|
|---|
| 13381 | * @param records The records to convert to a layer
|
|---|
| 13382 | * @throws IOException - if an IO error occurs
|
|---|
| 13383 | */
|
|---|
| 13384 | - public Layer(Collection<ProtoBufRecord> records) throws IOException {
|
|---|
| 13385 | + public Layer(Collection<ProtobufRecord> records) throws IOException {
|
|---|
| 13386 | // Do the unique required fields first
|
|---|
| 13387 | - Map<Integer, List<ProtoBufRecord>> sorted = records.stream().collect(Collectors.groupingBy(ProtoBufRecord::getField));
|
|---|
| 13388 | + Map<Integer, List<ProtobufRecord>> sorted = records.stream().collect(Collectors.groupingBy(ProtobufRecord::getField));
|
|---|
| 13389 | this.version = sorted.getOrDefault((int) VERSION_FIELD, Collections.emptyList()).parallelStream()
|
|---|
| 13390 | - .map(ProtoBufRecord::asUnsignedVarInt).map(Number::byteValue).findFirst().orElse(DEFAULT_VERSION);
|
|---|
| 13391 | + .map(ProtobufRecord::asUnsignedVarInt).map(Number::byteValue).findFirst().orElse(DEFAULT_VERSION);
|
|---|
| 13392 | // Per spec, we cannot continue past this until we have checked the version number
|
|---|
| 13393 | if (this.version != 1 && this.version != 2) {
|
|---|
| 13394 | throw new IllegalArgumentException(tr("We do not understand version {0} of the vector tile specification", this.version));
|
|---|
| 13395 | }
|
|---|
| 13396 | - this.name = sorted.getOrDefault((int) NAME_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asString).findFirst()
|
|---|
| 13397 | + this.name = sorted.getOrDefault((int) NAME_FIELD, Collections.emptyList()).parallelStream().map(ProtobufRecord::asString).findFirst()
|
|---|
| 13398 | .orElseThrow(() -> new IllegalArgumentException(tr("Vector tile layers must have a layer name")));
|
|---|
| 13399 | - this.extent = sorted.getOrDefault((int) EXTENT_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asUnsignedVarInt)
|
|---|
| 13400 | + this.extent = sorted.getOrDefault((int) EXTENT_FIELD, Collections.emptyList()).parallelStream().map(ProtobufRecord::asUnsignedVarInt)
|
|---|
| 13401 | .map(Number::intValue).findAny().orElse(DEFAULT_EXTENT);
|
|---|
| 13402 |
|
|---|
| 13403 | - sorted.getOrDefault((int) KEY_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::asString)
|
|---|
| 13404 | + sorted.getOrDefault((int) KEY_FIELD, Collections.emptyList()).parallelStream().map(ProtobufRecord::asString)
|
|---|
| 13405 | .forEachOrdered(this.keyList::add);
|
|---|
| 13406 | - sorted.getOrDefault((int) VALUE_FIELD, Collections.emptyList()).parallelStream().map(ProtoBufRecord::getBytes)
|
|---|
| 13407 | - .map(ProtoBufParser::new).map(parser1 -> {
|
|---|
| 13408 | + sorted.getOrDefault((int) VALUE_FIELD, Collections.emptyList()).parallelStream().map(ProtobufRecord::getBytes)
|
|---|
| 13409 | + .map(ProtobufParser::new).map(parser1 -> {
|
|---|
| 13410 | try {
|
|---|
| 13411 | - return new ProtoBufRecord(parser1);
|
|---|
| 13412 | + return new ProtobufRecord(parser1);
|
|---|
| 13413 | } catch (IOException e) {
|
|---|
| 13414 | Logging.error(e);
|
|---|
| 13415 | return null;
|
|---|
| 13416 | @@ -141,7 +141,7 @@ public final class Layer implements Destroyable {
|
|---|
| 13417 | throw exceptions.iterator().next();
|
|---|
| 13418 | }
|
|---|
| 13419 | // Cleanup bytes (for memory)
|
|---|
| 13420 | - for (ProtoBufRecord record : records) {
|
|---|
| 13421 | + for (ProtobufRecord record : records) {
|
|---|
| 13422 | record.close();
|
|---|
| 13423 | }
|
|---|
| 13424 | }
|
|---|
| 13425 | @@ -152,8 +152,8 @@ public final class Layer implements Destroyable {
|
|---|
| 13426 | * @return All the protobuf records
|
|---|
| 13427 | * @throws IOException If there was an error reading the bytes (unlikely)
|
|---|
| 13428 | */
|
|---|
| 13429 | - private static Collection<ProtoBufRecord> getAllRecords(byte[] bytes) throws IOException {
|
|---|
| 13430 | - try (ProtoBufParser parser = new ProtoBufParser(bytes)) {
|
|---|
| 13431 | + private static Collection<ProtobufRecord> getAllRecords(byte[] bytes) throws IOException {
|
|---|
| 13432 | + try (ProtobufParser parser = new ProtobufParser(bytes)) {
|
|---|
| 13433 | return parser.allRecords();
|
|---|
| 13434 | }
|
|---|
| 13435 | }
|
|---|
| 13436 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 13437 | index 0743bec34..522038c77 100644
|
|---|
| 13438 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 13439 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTTile.java
|
|---|
| 13440 | @@ -7,8 +7,8 @@ import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
|
|---|
| 13441 | import org.openstreetmap.josm.data.IQuadBucketType;
|
|---|
| 13442 | import org.openstreetmap.josm.data.imagery.vectortile.VectorTile;
|
|---|
| 13443 | import org.openstreetmap.josm.data.osm.BBox;
|
|---|
| 13444 | -import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 13445 | -import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 13446 | +import org.openstreetmap.josm.data.protobuf.ProtobufParser;
|
|---|
| 13447 | +import org.openstreetmap.josm.data.protobuf.ProtobufRecord;
|
|---|
| 13448 | import org.openstreetmap.josm.data.vector.VectorDataStore;
|
|---|
| 13449 | import org.openstreetmap.josm.tools.ListenerList;
|
|---|
| 13450 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 13451 | @@ -50,13 +50,13 @@ public class MVTTile extends Tile implements VectorTile, IQuadBucketType {
|
|---|
| 13452 | public void loadImage(final InputStream inputStream) throws IOException {
|
|---|
| 13453 | if (this.image == null || this.image == Tile.LOADING_IMAGE || this.image == Tile.ERROR_IMAGE) {
|
|---|
| 13454 | this.initLoading();
|
|---|
| 13455 | - ProtoBufParser parser = new ProtoBufParser(inputStream);
|
|---|
| 13456 | - Collection<ProtoBufRecord> protoBufRecords = parser.allRecords();
|
|---|
| 13457 | + ProtobufParser parser = new ProtobufParser(inputStream);
|
|---|
| 13458 | + Collection<ProtobufRecord> protobufRecords = parser.allRecords();
|
|---|
| 13459 | this.layers = new HashSet<>();
|
|---|
| 13460 | - this.layers = protoBufRecords.stream().map(protoBufRecord -> {
|
|---|
| 13461 | + this.layers = protobufRecords.stream().map(protoBufRecord -> {
|
|---|
| 13462 | Layer mvtLayer = null;
|
|---|
| 13463 | if (protoBufRecord.getField() == Layer.LAYER_FIELD) {
|
|---|
| 13464 | - try (ProtoBufParser tParser = new ProtoBufParser(protoBufRecord.getBytes())) {
|
|---|
| 13465 | + try (ProtobufParser tParser = new ProtobufParser(protoBufRecord.getBytes())) {
|
|---|
| 13466 | mvtLayer = new Layer(tParser.allRecords());
|
|---|
| 13467 | } catch (IOException e) {
|
|---|
| 13468 | Logging.error(e);
|
|---|
| 13469 | diff --git a/src/org/openstreetmap/josm/data/protobuf/ProtoBufPacked.java b/src/org/openstreetmap/josm/data/protobuf/ProtobufPacked.java
|
|---|
| 13470 | similarity index 79%
|
|---|
| 13471 | rename from src/org/openstreetmap/josm/data/protobuf/ProtoBufPacked.java
|
|---|
| 13472 | rename to src/org/openstreetmap/josm/data/protobuf/ProtobufPacked.java
|
|---|
| 13473 | index 109f8915a..a12bbe6b8 100644
|
|---|
| 13474 | --- a/src/org/openstreetmap/josm/data/protobuf/ProtoBufPacked.java
|
|---|
| 13475 | +++ b/src/org/openstreetmap/josm/data/protobuf/ProtobufPacked.java
|
|---|
| 13476 | @@ -10,22 +10,22 @@ import java.util.List;
|
|---|
| 13477 | * @author Taylor Smock
|
|---|
| 13478 | * @since xxx
|
|---|
| 13479 | */
|
|---|
| 13480 | -public class ProtoBufPacked {
|
|---|
| 13481 | +public class ProtobufPacked {
|
|---|
| 13482 | private final byte[] bytes;
|
|---|
| 13483 | private final Number[] numbers;
|
|---|
| 13484 | private int location;
|
|---|
| 13485 |
|
|---|
| 13486 | /**
|
|---|
| 13487 | - * Create a new ProtoBufPacked object
|
|---|
| 13488 | + * Create a new ProtobufPacked object
|
|---|
| 13489 | *
|
|---|
| 13490 | * @param bytes The packed bytes
|
|---|
| 13491 | */
|
|---|
| 13492 | - public ProtoBufPacked(byte[] bytes) {
|
|---|
| 13493 | + public ProtobufPacked(byte[] bytes) {
|
|---|
| 13494 | this.location = 0;
|
|---|
| 13495 | this.bytes = bytes;
|
|---|
| 13496 | List<Number> numbersT = new ArrayList<>();
|
|---|
| 13497 | while (this.location < bytes.length) {
|
|---|
| 13498 | - numbersT.add(ProtoBufParser.convertByteArray(this.nextVarInt(), ProtoBufParser.VAR_INT_BYTE_SIZE));
|
|---|
| 13499 | + numbersT.add(ProtobufParser.convertByteArray(this.nextVarInt(), ProtobufParser.VAR_INT_BYTE_SIZE));
|
|---|
| 13500 | }
|
|---|
| 13501 |
|
|---|
| 13502 | this.numbers = new Number[numbersT.size()];
|
|---|
| 13503 | @@ -45,10 +45,10 @@ public class ProtoBufPacked {
|
|---|
| 13504 |
|
|---|
| 13505 | private byte[] nextVarInt() {
|
|---|
| 13506 | List<Byte> byteList = new ArrayList<>();
|
|---|
| 13507 | - while ((this.bytes[this.location] & ProtoBufParser.MOST_SIGNIFICANT_BYTE)
|
|---|
| 13508 | - == ProtoBufParser.MOST_SIGNIFICANT_BYTE) {
|
|---|
| 13509 | + while ((this.bytes[this.location] & ProtobufParser.MOST_SIGNIFICANT_BYTE)
|
|---|
| 13510 | + == ProtobufParser.MOST_SIGNIFICANT_BYTE) {
|
|---|
| 13511 | // Get rid of the leading bit (shift left 1, then shift right 1 unsigned)
|
|---|
| 13512 | - byteList.add((byte) (this.bytes[this.location++] ^ ProtoBufParser.MOST_SIGNIFICANT_BYTE));
|
|---|
| 13513 | + byteList.add((byte) (this.bytes[this.location++] ^ ProtobufParser.MOST_SIGNIFICANT_BYTE));
|
|---|
| 13514 | }
|
|---|
| 13515 | // The last byte doesn't drop the most significant bit
|
|---|
| 13516 | byteList.add(this.bytes[this.location++]);
|
|---|
| 13517 | diff --git a/src/org/openstreetmap/josm/data/protobuf/ProtoBufParser.java b/src/org/openstreetmap/josm/data/protobuf/ProtobufParser.java
|
|---|
| 13518 | similarity index 95%
|
|---|
| 13519 | rename from src/org/openstreetmap/josm/data/protobuf/ProtoBufParser.java
|
|---|
| 13520 | rename to src/org/openstreetmap/josm/data/protobuf/ProtobufParser.java
|
|---|
| 13521 | index 18059e339..8364cc3b7 100644
|
|---|
| 13522 | --- a/src/org/openstreetmap/josm/data/protobuf/ProtoBufParser.java
|
|---|
| 13523 | +++ b/src/org/openstreetmap/josm/data/protobuf/ProtobufParser.java
|
|---|
| 13524 | @@ -17,7 +17,7 @@ import org.openstreetmap.josm.tools.Logging;
|
|---|
| 13525 | * @author Taylor Smock
|
|---|
| 13526 | * @since xxx
|
|---|
| 13527 | */
|
|---|
| 13528 | -public class ProtoBufParser implements AutoCloseable {
|
|---|
| 13529 | +public class ProtobufParser implements AutoCloseable {
|
|---|
| 13530 | /**
|
|---|
| 13531 | * The default byte size (see {@link #VAR_INT_BYTE_SIZE} for var ints)
|
|---|
| 13532 | */
|
|---|
| 13533 | @@ -96,7 +96,7 @@ public class ProtoBufParser implements AutoCloseable {
|
|---|
| 13534 | *
|
|---|
| 13535 | * @param bytes The bytes to parse
|
|---|
| 13536 | */
|
|---|
| 13537 | - public ProtoBufParser(byte[] bytes) {
|
|---|
| 13538 | + public ProtobufParser(byte[] bytes) {
|
|---|
| 13539 | this(new ByteArrayInputStream(bytes));
|
|---|
| 13540 | }
|
|---|
| 13541 |
|
|---|
| 13542 | @@ -105,7 +105,7 @@ public class ProtoBufParser implements AutoCloseable {
|
|---|
| 13543 | *
|
|---|
| 13544 | * @param inputStream The InputStream (will be fully read at this time)
|
|---|
| 13545 | */
|
|---|
| 13546 | - public ProtoBufParser(InputStream inputStream) {
|
|---|
| 13547 | + public ProtobufParser(InputStream inputStream) {
|
|---|
| 13548 | if (inputStream.markSupported()) {
|
|---|
| 13549 | this.inputStream = inputStream;
|
|---|
| 13550 | } else {
|
|---|
| 13551 | @@ -119,10 +119,10 @@ public class ProtoBufParser implements AutoCloseable {
|
|---|
| 13552 | * @return A collection of all records
|
|---|
| 13553 | * @throws IOException - if an IO error occurs
|
|---|
| 13554 | */
|
|---|
| 13555 | - public Collection<ProtoBufRecord> allRecords() throws IOException {
|
|---|
| 13556 | - Collection<ProtoBufRecord> records = new ArrayList<>();
|
|---|
| 13557 | + public Collection<ProtobufRecord> allRecords() throws IOException {
|
|---|
| 13558 | + Collection<ProtobufRecord> records = new ArrayList<>();
|
|---|
| 13559 | while (this.hasNext()) {
|
|---|
| 13560 | - records.add(new ProtoBufRecord(this));
|
|---|
| 13561 | + records.add(new ProtobufRecord(this));
|
|---|
| 13562 | }
|
|---|
| 13563 | return records;
|
|---|
| 13564 | }
|
|---|
| 13565 | diff --git a/src/org/openstreetmap/josm/data/protobuf/ProtoBufRecord.java b/src/org/openstreetmap/josm/data/protobuf/ProtobufRecord.java
|
|---|
| 13566 | similarity index 86%
|
|---|
| 13567 | rename from src/org/openstreetmap/josm/data/protobuf/ProtoBufRecord.java
|
|---|
| 13568 | rename to src/org/openstreetmap/josm/data/protobuf/ProtobufRecord.java
|
|---|
| 13569 | index 1eb5d38a6..e760996d4 100644
|
|---|
| 13570 | --- a/src/org/openstreetmap/josm/data/protobuf/ProtoBufRecord.java
|
|---|
| 13571 | +++ b/src/org/openstreetmap/josm/data/protobuf/ProtobufRecord.java
|
|---|
| 13572 | @@ -13,7 +13,7 @@ import org.openstreetmap.josm.tools.Utils;
|
|---|
| 13573 | * @author Taylor Smock
|
|---|
| 13574 | * @since xxx
|
|---|
| 13575 | */
|
|---|
| 13576 | -public class ProtoBufRecord implements AutoCloseable {
|
|---|
| 13577 | +public class ProtobufRecord implements AutoCloseable {
|
|---|
| 13578 | private static final byte[] EMPTY_BYTES = {};
|
|---|
| 13579 | private final WireType type;
|
|---|
| 13580 | private final int field;
|
|---|
| 13581 | @@ -25,8 +25,8 @@ public class ProtoBufRecord implements AutoCloseable {
|
|---|
| 13582 | * @param parser The parser to use to create the record
|
|---|
| 13583 | * @throws IOException - if an IO error occurs
|
|---|
| 13584 | */
|
|---|
| 13585 | - public ProtoBufRecord(ProtoBufParser parser) throws IOException {
|
|---|
| 13586 | - Number number = ProtoBufParser.convertByteArray(parser.nextVarInt(), ProtoBufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 13587 | + public ProtobufRecord(ProtobufParser parser) throws IOException {
|
|---|
| 13588 | + Number number = ProtobufParser.convertByteArray(parser.nextVarInt(), ProtobufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 13589 | // I don't foresee having field numbers > {@code Integer#MAX_VALUE >> 3}
|
|---|
| 13590 | this.field = (int) number.longValue() >> 3;
|
|---|
| 13591 | // 7 is 111 (so last three bits)
|
|---|
| 13592 | @@ -53,7 +53,7 @@ public class ProtoBufRecord implements AutoCloseable {
|
|---|
| 13593 | * @return the double
|
|---|
| 13594 | */
|
|---|
| 13595 | public double asDouble() {
|
|---|
| 13596 | - long doubleNumber = ProtoBufParser.convertByteArray(asFixed64(), ProtoBufParser.BYTE_SIZE).longValue();
|
|---|
| 13597 | + long doubleNumber = ProtobufParser.convertByteArray(asFixed64(), ProtobufParser.BYTE_SIZE).longValue();
|
|---|
| 13598 | return Double.longBitsToDouble(doubleNumber);
|
|---|
| 13599 | }
|
|---|
| 13600 |
|
|---|
| 13601 | @@ -85,7 +85,7 @@ public class ProtoBufRecord implements AutoCloseable {
|
|---|
| 13602 | * @return the float
|
|---|
| 13603 | */
|
|---|
| 13604 | public float asFloat() {
|
|---|
| 13605 | - int floatNumber = ProtoBufParser.convertByteArray(asFixed32(), ProtoBufParser.BYTE_SIZE).intValue();
|
|---|
| 13606 | + int floatNumber = ProtobufParser.convertByteArray(asFixed32(), ProtobufParser.BYTE_SIZE).intValue();
|
|---|
| 13607 | return Float.intBitsToFloat(floatNumber);
|
|---|
| 13608 | }
|
|---|
| 13609 |
|
|---|
| 13610 | @@ -97,7 +97,7 @@ public class ProtoBufRecord implements AutoCloseable {
|
|---|
| 13611 | */
|
|---|
| 13612 | public Number asSignedVarInt() {
|
|---|
| 13613 | final Number signed = this.asUnsignedVarInt();
|
|---|
| 13614 | - return ProtoBufParser.decodeZigZag(signed);
|
|---|
| 13615 | + return ProtobufParser.decodeZigZag(signed);
|
|---|
| 13616 | }
|
|---|
| 13617 |
|
|---|
| 13618 | /**
|
|---|
| 13619 | @@ -115,7 +115,7 @@ public class ProtoBufRecord implements AutoCloseable {
|
|---|
| 13620 | * @return The var int ({@code int32}, {@code int64}, {@code uint32}, {@code uint64}, {@code bool}, {@code enum})
|
|---|
| 13621 | */
|
|---|
| 13622 | public Number asUnsignedVarInt() {
|
|---|
| 13623 | - return ProtoBufParser.convertByteArray(this.bytes, ProtoBufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 13624 | + return ProtobufParser.convertByteArray(this.bytes, ProtobufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 13625 | }
|
|---|
| 13626 |
|
|---|
| 13627 | @Override
|
|---|
| 13628 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 13629 | index 61e21dc60..e9b5bae8d 100644
|
|---|
| 13630 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 13631 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/LayerTest.java
|
|---|
| 13632 | @@ -11,8 +11,8 @@ import java.util.Arrays;
|
|---|
| 13633 | import java.util.List;
|
|---|
| 13634 |
|
|---|
| 13635 | import org.openstreetmap.josm.TestUtils;
|
|---|
| 13636 | -import org.openstreetmap.josm.data.protobuf.ProtoBufParser;
|
|---|
| 13637 | -import org.openstreetmap.josm.data.protobuf.ProtoBufRecord;
|
|---|
| 13638 | +import org.openstreetmap.josm.data.protobuf.ProtobufParser;
|
|---|
| 13639 | +import org.openstreetmap.josm.data.protobuf.ProtobufRecord;
|
|---|
| 13640 |
|
|---|
| 13641 | import nl.jqno.equalsverifier.EqualsVerifier;
|
|---|
| 13642 | import org.junit.jupiter.api.Test;
|
|---|
| 13643 | @@ -66,14 +66,14 @@ public class LayerTest {
|
|---|
| 13644 | * @throws IOException If something happened (should never trigger)
|
|---|
| 13645 | */
|
|---|
| 13646 | static Layer getLayer(byte[] bytes) throws IOException {
|
|---|
| 13647 | - List<ProtoBufRecord> records = (List<ProtoBufRecord>) new ProtoBufParser(bytes).allRecords();
|
|---|
| 13648 | + List<ProtobufRecord> records = (List<ProtobufRecord>) new ProtobufParser(bytes).allRecords();
|
|---|
| 13649 | assertEquals(1, records.size());
|
|---|
| 13650 | - return new Layer(new ProtoBufParser(records.get(0).getBytes()).allRecords());
|
|---|
| 13651 | + return new Layer(new ProtobufParser(records.get(0).getBytes()).allRecords());
|
|---|
| 13652 | }
|
|---|
| 13653 |
|
|---|
| 13654 | @Test
|
|---|
| 13655 | void testLayerCreation() throws IOException {
|
|---|
| 13656 | - List<ProtoBufRecord> layers = (List<ProtoBufRecord>) new ProtoBufParser(new FileInputStream(TestUtils.getTestDataRoot()
|
|---|
| 13657 | + List<ProtobufRecord> layers = (List<ProtobufRecord>) new ProtobufParser(new FileInputStream(TestUtils.getTestDataRoot()
|
|---|
| 13658 | + "pbf/mapillary/14/3249/6258.mvt")).allRecords();
|
|---|
| 13659 | Layer sequenceLayer = new Layer(layers.get(0).getBytes());
|
|---|
| 13660 | assertEquals("mapillary-sequences", sequenceLayer.getName());
|
|---|
| 13661 | @@ -92,7 +92,7 @@ public class LayerTest {
|
|---|
| 13662 |
|
|---|
| 13663 | @Test
|
|---|
| 13664 | void testLayerEqualsHashCode() throws IOException {
|
|---|
| 13665 | - List<ProtoBufRecord> layers = (List<ProtoBufRecord>) new ProtoBufParser(new FileInputStream(TestUtils.getTestDataRoot()
|
|---|
| 13666 | + List<ProtobufRecord> layers = (List<ProtobufRecord>) new ProtobufParser(new FileInputStream(TestUtils.getTestDataRoot()
|
|---|
| 13667 | + "pbf/mapillary/14/3249/6258.mvt")).allRecords();
|
|---|
| 13668 | EqualsVerifier.forClass(Layer.class).withPrefabValues(byte[].class, layers.get(0).getBytes(), layers.get(1).getBytes())
|
|---|
| 13669 | .verify();
|
|---|
| 13670 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java
|
|---|
| 13671 | deleted file mode 100644
|
|---|
| 13672 | index bdfdf86b7..000000000
|
|---|
| 13673 | --- a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufParserTest.java
|
|---|
| 13674 | +++ /dev/null
|
|---|
| 13675 | @@ -1,51 +0,0 @@
|
|---|
| 13676 | -// License: GPL. For details, see LICENSE file.
|
|---|
| 13677 | -package org.openstreetmap.josm.data.protobuf;
|
|---|
| 13678 | -
|
|---|
| 13679 | -import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 13680 | -
|
|---|
| 13681 | -import org.junit.jupiter.api.Test;
|
|---|
| 13682 | -
|
|---|
| 13683 | -/**
|
|---|
| 13684 | - * Test class for {@link ProtoBufParser}
|
|---|
| 13685 | - * @author Taylor Smock
|
|---|
| 13686 | - * @since xxx
|
|---|
| 13687 | - */
|
|---|
| 13688 | -class ProtoBufParserTest {
|
|---|
| 13689 | - /**
|
|---|
| 13690 | - * Check that we are appropriately converting values to the "smallest" type
|
|---|
| 13691 | - */
|
|---|
| 13692 | - @Test
|
|---|
| 13693 | - void testConvertLong() {
|
|---|
| 13694 | - // No casting due to auto conversions
|
|---|
| 13695 | - assertEquals(Byte.MAX_VALUE, ProtoBufParser.convertLong(Byte.MAX_VALUE));
|
|---|
| 13696 | - assertEquals(Byte.MIN_VALUE, ProtoBufParser.convertLong(Byte.MIN_VALUE));
|
|---|
| 13697 | - assertEquals(Short.MIN_VALUE, ProtoBufParser.convertLong(Short.MIN_VALUE));
|
|---|
| 13698 | - assertEquals(Short.MAX_VALUE, ProtoBufParser.convertLong(Short.MAX_VALUE));
|
|---|
| 13699 | - assertEquals(Integer.MAX_VALUE, ProtoBufParser.convertLong(Integer.MAX_VALUE));
|
|---|
| 13700 | - assertEquals(Integer.MIN_VALUE, ProtoBufParser.convertLong(Integer.MIN_VALUE));
|
|---|
| 13701 | - assertEquals(Long.MIN_VALUE, ProtoBufParser.convertLong(Long.MIN_VALUE));
|
|---|
| 13702 | - assertEquals(Long.MAX_VALUE, ProtoBufParser.convertLong(Long.MAX_VALUE));
|
|---|
| 13703 | - }
|
|---|
| 13704 | -
|
|---|
| 13705 | - /**
|
|---|
| 13706 | - * Check that zig zags are appropriately encoded.
|
|---|
| 13707 | - */
|
|---|
| 13708 | - @Test
|
|---|
| 13709 | - void testEncodeZigZag() {
|
|---|
| 13710 | - assertEquals(0, ProtoBufParser.encodeZigZag(0).byteValue());
|
|---|
| 13711 | - assertEquals(1, ProtoBufParser.encodeZigZag(-1).byteValue());
|
|---|
| 13712 | - assertEquals(2, ProtoBufParser.encodeZigZag(1).byteValue());
|
|---|
| 13713 | - assertEquals(3, ProtoBufParser.encodeZigZag(-2).byteValue());
|
|---|
| 13714 | - assertEquals(254, ProtoBufParser.encodeZigZag(Byte.MAX_VALUE).shortValue());
|
|---|
| 13715 | - assertEquals(255, ProtoBufParser.encodeZigZag(Byte.MIN_VALUE).shortValue());
|
|---|
| 13716 | - assertEquals(65_534, ProtoBufParser.encodeZigZag(Short.MAX_VALUE).intValue());
|
|---|
| 13717 | - assertEquals(65_535, ProtoBufParser.encodeZigZag(Short.MIN_VALUE).intValue());
|
|---|
| 13718 | - // These integers check a possible boundary condition (the boundary between using the 32/64 bit encoding methods)
|
|---|
| 13719 | - assertEquals(4_294_967_292L, ProtoBufParser.encodeZigZag(Integer.MAX_VALUE - 1).longValue());
|
|---|
| 13720 | - assertEquals(4_294_967_293L, ProtoBufParser.encodeZigZag(Integer.MIN_VALUE + 1).longValue());
|
|---|
| 13721 | - assertEquals(4_294_967_294L, ProtoBufParser.encodeZigZag(Integer.MAX_VALUE).longValue());
|
|---|
| 13722 | - assertEquals(4_294_967_295L, ProtoBufParser.encodeZigZag(Integer.MIN_VALUE).longValue());
|
|---|
| 13723 | - assertEquals(4_294_967_296L, ProtoBufParser.encodeZigZag(Integer.MAX_VALUE + 1L).longValue());
|
|---|
| 13724 | - assertEquals(4_294_967_297L, ProtoBufParser.encodeZigZag(Integer.MIN_VALUE - 1L).longValue());
|
|---|
| 13725 | - }
|
|---|
| 13726 | -}
|
|---|
| 13727 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 13728 | index 043481efe..e5cd8c738 100644
|
|---|
| 13729 | --- a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 13730 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 13731 | @@ -37,7 +37,7 @@ import org.junit.jupiter.api.Test;
|
|---|
| 13732 | import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 13733 |
|
|---|
| 13734 | /**
|
|---|
| 13735 | - * Test class for {@link ProtoBufParser} and {@link ProtoBufRecord}
|
|---|
| 13736 | + * Test class for {@link ProtobufParser} and {@link ProtobufRecord}
|
|---|
| 13737 | *
|
|---|
| 13738 | * @author Taylor Smock
|
|---|
| 13739 | * @since xxx
|
|---|
| 13740 | @@ -67,7 +67,7 @@ class ProtoBufTest {
|
|---|
| 13741 | for (int i = 0; i < bytes.length; i++) {
|
|---|
| 13742 | byteArray[i] = (byte) bytes[i];
|
|---|
| 13743 | }
|
|---|
| 13744 | - return ProtoBufParser.convertByteArray(byteArray, ProtoBufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 13745 | + return ProtobufParser.convertByteArray(byteArray, ProtobufParser.VAR_INT_BYTE_SIZE);
|
|---|
| 13746 | }
|
|---|
| 13747 |
|
|---|
| 13748 | /**
|
|---|
| 13749 | @@ -79,10 +79,10 @@ class ProtoBufTest {
|
|---|
| 13750 | void testRead_14_3248_6258() throws IOException {
|
|---|
| 13751 | File vectorTile = Paths.get(TestUtils.getTestDataRoot(), "pbf", "mapillary", "14", "3248", "6258.mvt").toFile();
|
|---|
| 13752 | InputStream inputStream = Compression.getUncompressedFileInputStream(vectorTile);
|
|---|
| 13753 | - Collection<ProtoBufRecord> records = new ProtoBufParser(inputStream).allRecords();
|
|---|
| 13754 | + Collection<ProtobufRecord> records = new ProtobufParser(inputStream).allRecords();
|
|---|
| 13755 | assertEquals(2, records.size());
|
|---|
| 13756 | List<Layer> layers = new ArrayList<>();
|
|---|
| 13757 | - for (ProtoBufRecord record : records) {
|
|---|
| 13758 | + for (ProtobufRecord record : records) {
|
|---|
| 13759 | if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 13760 | layers.add(new Layer(record.getBytes()));
|
|---|
| 13761 | } else {
|
|---|
| 13762 | @@ -112,9 +112,9 @@ class ProtoBufTest {
|
|---|
| 13763 | File vectorTile = Paths.get(TestUtils.getTestDataRoot(), "pbf", "openinframap", "17", "26028", "50060.pbf")
|
|---|
| 13764 | .toFile();
|
|---|
| 13765 | InputStream inputStream = Compression.getUncompressedFileInputStream(vectorTile);
|
|---|
| 13766 | - Collection<ProtoBufRecord> records = new ProtoBufParser(inputStream).allRecords();
|
|---|
| 13767 | + Collection<ProtobufRecord> records = new ProtobufParser(inputStream).allRecords();
|
|---|
| 13768 | List<Layer> layers = new ArrayList<>();
|
|---|
| 13769 | - for (ProtoBufRecord record : records) {
|
|---|
| 13770 | + for (ProtobufRecord record : records) {
|
|---|
| 13771 | if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 13772 | layers.add(new Layer(record.getBytes()));
|
|---|
| 13773 | } else {
|
|---|
| 13774 | @@ -155,12 +155,12 @@ class ProtoBufTest {
|
|---|
| 13775 |
|
|---|
| 13776 | @Test
|
|---|
| 13777 | void testReadVarInt() {
|
|---|
| 13778 | - assertEquals(ProtoBufParser.convertLong(0), bytesToVarInt(0x0));
|
|---|
| 13779 | - assertEquals(ProtoBufParser.convertLong(1), bytesToVarInt(0x1));
|
|---|
| 13780 | - assertEquals(ProtoBufParser.convertLong(127), bytesToVarInt(0x7f));
|
|---|
| 13781 | + assertEquals(ProtobufParser.convertLong(0), bytesToVarInt(0x0));
|
|---|
| 13782 | + assertEquals(ProtobufParser.convertLong(1), bytesToVarInt(0x1));
|
|---|
| 13783 | + assertEquals(ProtobufParser.convertLong(127), bytesToVarInt(0x7f));
|
|---|
| 13784 | // This should b 0xff 0xff 0xff 0xff 0x07, but we drop the leading bit when reading to a byte array
|
|---|
| 13785 | Number actual = bytesToVarInt(0x7f, 0x7f, 0x7f, 0x7f, 0x07);
|
|---|
| 13786 | - assertEquals(ProtoBufParser.convertLong(Integer.MAX_VALUE), actual,
|
|---|
| 13787 | + assertEquals(ProtobufParser.convertLong(Integer.MAX_VALUE), actual,
|
|---|
| 13788 | MessageFormat.format("Expected {0} but got {1}", Integer.toBinaryString(Integer.MAX_VALUE),
|
|---|
| 13789 | Long.toBinaryString(actual.longValue())));
|
|---|
| 13790 | }
|
|---|
| 13791 | @@ -173,21 +173,21 @@ class ProtoBufTest {
|
|---|
| 13792 | */
|
|---|
| 13793 | @Test
|
|---|
| 13794 | void testSimpleMessage() throws IOException {
|
|---|
| 13795 | - ProtoBufParser parser = new ProtoBufParser(new byte[] {(byte) 0x08, (byte) 0x96, (byte) 0x01});
|
|---|
| 13796 | - ProtoBufRecord record = new ProtoBufRecord(parser);
|
|---|
| 13797 | + ProtobufParser parser = new ProtobufParser(new byte[] {(byte) 0x08, (byte) 0x96, (byte) 0x01});
|
|---|
| 13798 | + ProtobufRecord record = new ProtobufRecord(parser);
|
|---|
| 13799 | assertEquals(WireType.VARINT, record.getType());
|
|---|
| 13800 | assertEquals(150, record.asUnsignedVarInt().intValue());
|
|---|
| 13801 | }
|
|---|
| 13802 |
|
|---|
| 13803 | @Test
|
|---|
| 13804 | void testSingletonMultiPoint() throws IOException {
|
|---|
| 13805 | - Collection<ProtoBufRecord> records = new ProtoBufParser(new ByteArrayInputStream(toByteArray(
|
|---|
| 13806 | + Collection<ProtobufRecord> records = new ProtobufParser(new ByteArrayInputStream(toByteArray(
|
|---|
| 13807 | new int[] {0x1a, 0x2c, 0x78, 0x02, 0x0a, 0x03, 0x74, 0x6d, 0x70, 0x28, 0x80, 0x20, 0x1a, 0x04, 0x6e,
|
|---|
| 13808 | 0x61, 0x6d, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65,
|
|---|
| 13809 | 0x12, 0x0d, 0x18, 0x01, 0x12, 0x02, 0x00, 0x00, 0x22, 0x05, 0x09, 0xe0, 0x3e, 0x84, 0x27})))
|
|---|
| 13810 | .allRecords();
|
|---|
| 13811 | List<Layer> layers = new ArrayList<>();
|
|---|
| 13812 | - for (ProtoBufRecord record : records) {
|
|---|
| 13813 | + for (ProtobufRecord record : records) {
|
|---|
| 13814 | if (record.getField() == Layer.LAYER_FIELD) {
|
|---|
| 13815 | layers.add(new Layer(record.getBytes()));
|
|---|
| 13816 | } else {
|
|---|
| 13817 | @@ -203,9 +203,9 @@ class ProtoBufTest {
|
|---|
| 13818 |
|
|---|
| 13819 | @Test
|
|---|
| 13820 | void testZigZag() {
|
|---|
| 13821 | - assertEquals(0, ProtoBufParser.decodeZigZag(0).intValue());
|
|---|
| 13822 | - assertEquals(-1, ProtoBufParser.decodeZigZag(1).intValue());
|
|---|
| 13823 | - assertEquals(1, ProtoBufParser.decodeZigZag(2).intValue());
|
|---|
| 13824 | - assertEquals(-2, ProtoBufParser.decodeZigZag(3).intValue());
|
|---|
| 13825 | + assertEquals(0, ProtobufParser.decodeZigZag(0).intValue());
|
|---|
| 13826 | + assertEquals(-1, ProtobufParser.decodeZigZag(1).intValue());
|
|---|
| 13827 | + assertEquals(1, ProtobufParser.decodeZigZag(2).intValue());
|
|---|
| 13828 | + assertEquals(-2, ProtobufParser.decodeZigZag(3).intValue());
|
|---|
| 13829 | }
|
|---|
| 13830 | }
|
|---|
| 13831 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufParserTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufParserTest.java
|
|---|
| 13832 | new file mode 100644
|
|---|
| 13833 | index 000000000..201644076
|
|---|
| 13834 | --- /dev/null
|
|---|
| 13835 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufParserTest.java
|
|---|
| 13836 | @@ -0,0 +1,51 @@
|
|---|
| 13837 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 13838 | +package org.openstreetmap.josm.data.protobuf;
|
|---|
| 13839 | +
|
|---|
| 13840 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 13841 | +
|
|---|
| 13842 | +import org.junit.jupiter.api.Test;
|
|---|
| 13843 | +
|
|---|
| 13844 | +/**
|
|---|
| 13845 | + * Test class for {@link ProtobufParser}
|
|---|
| 13846 | + * @author Taylor Smock
|
|---|
| 13847 | + * @since xxx
|
|---|
| 13848 | + */
|
|---|
| 13849 | +class ProtobufParserTest {
|
|---|
| 13850 | + /**
|
|---|
| 13851 | + * Check that we are appropriately converting values to the "smallest" type
|
|---|
| 13852 | + */
|
|---|
| 13853 | + @Test
|
|---|
| 13854 | + void testConvertLong() {
|
|---|
| 13855 | + // No casting due to auto conversions
|
|---|
| 13856 | + assertEquals(Byte.MAX_VALUE, ProtobufParser.convertLong(Byte.MAX_VALUE));
|
|---|
| 13857 | + assertEquals(Byte.MIN_VALUE, ProtobufParser.convertLong(Byte.MIN_VALUE));
|
|---|
| 13858 | + assertEquals(Short.MIN_VALUE, ProtobufParser.convertLong(Short.MIN_VALUE));
|
|---|
| 13859 | + assertEquals(Short.MAX_VALUE, ProtobufParser.convertLong(Short.MAX_VALUE));
|
|---|
| 13860 | + assertEquals(Integer.MAX_VALUE, ProtobufParser.convertLong(Integer.MAX_VALUE));
|
|---|
| 13861 | + assertEquals(Integer.MIN_VALUE, ProtobufParser.convertLong(Integer.MIN_VALUE));
|
|---|
| 13862 | + assertEquals(Long.MIN_VALUE, ProtobufParser.convertLong(Long.MIN_VALUE));
|
|---|
| 13863 | + assertEquals(Long.MAX_VALUE, ProtobufParser.convertLong(Long.MAX_VALUE));
|
|---|
| 13864 | + }
|
|---|
| 13865 | +
|
|---|
| 13866 | + /**
|
|---|
| 13867 | + * Check that zig zags are appropriately encoded.
|
|---|
| 13868 | + */
|
|---|
| 13869 | + @Test
|
|---|
| 13870 | + void testEncodeZigZag() {
|
|---|
| 13871 | + assertEquals(0, ProtobufParser.encodeZigZag(0).byteValue());
|
|---|
| 13872 | + assertEquals(1, ProtobufParser.encodeZigZag(-1).byteValue());
|
|---|
| 13873 | + assertEquals(2, ProtobufParser.encodeZigZag(1).byteValue());
|
|---|
| 13874 | + assertEquals(3, ProtobufParser.encodeZigZag(-2).byteValue());
|
|---|
| 13875 | + assertEquals(254, ProtobufParser.encodeZigZag(Byte.MAX_VALUE).shortValue());
|
|---|
| 13876 | + assertEquals(255, ProtobufParser.encodeZigZag(Byte.MIN_VALUE).shortValue());
|
|---|
| 13877 | + assertEquals(65_534, ProtobufParser.encodeZigZag(Short.MAX_VALUE).intValue());
|
|---|
| 13878 | + assertEquals(65_535, ProtobufParser.encodeZigZag(Short.MIN_VALUE).intValue());
|
|---|
| 13879 | + // These integers check a possible boundary condition (the boundary between using the 32/64 bit encoding methods)
|
|---|
| 13880 | + assertEquals(4_294_967_292L, ProtobufParser.encodeZigZag(Integer.MAX_VALUE - 1).longValue());
|
|---|
| 13881 | + assertEquals(4_294_967_293L, ProtobufParser.encodeZigZag(Integer.MIN_VALUE + 1).longValue());
|
|---|
| 13882 | + assertEquals(4_294_967_294L, ProtobufParser.encodeZigZag(Integer.MAX_VALUE).longValue());
|
|---|
| 13883 | + assertEquals(4_294_967_295L, ProtobufParser.encodeZigZag(Integer.MIN_VALUE).longValue());
|
|---|
| 13884 | + assertEquals(4_294_967_296L, ProtobufParser.encodeZigZag(Integer.MAX_VALUE + 1L).longValue());
|
|---|
| 13885 | + assertEquals(4_294_967_297L, ProtobufParser.encodeZigZag(Integer.MIN_VALUE - 1L).longValue());
|
|---|
| 13886 | + }
|
|---|
| 13887 | +}
|
|---|
| 13888 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufRecordTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufRecordTest.java
|
|---|
| 13889 | similarity index 67%
|
|---|
| 13890 | rename from test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufRecordTest.java
|
|---|
| 13891 | rename to test/unit/org/openstreetmap/josm/data/protobuf/ProtobufRecordTest.java
|
|---|
| 13892 | index d0e204c6a..6573d36fc 100644
|
|---|
| 13893 | --- a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufRecordTest.java
|
|---|
| 13894 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufRecordTest.java
|
|---|
| 13895 | @@ -9,21 +9,21 @@ import java.io.IOException;
|
|---|
| 13896 | import org.junit.jupiter.api.Test;
|
|---|
| 13897 |
|
|---|
| 13898 | /**
|
|---|
| 13899 | - * Test class for specific {@link ProtoBufRecord} functionality
|
|---|
| 13900 | + * Test class for specific {@link ProtobufRecord} functionality
|
|---|
| 13901 | */
|
|---|
| 13902 | -class ProtoBufRecordTest {
|
|---|
| 13903 | +class ProtobufRecordTest {
|
|---|
| 13904 | @Test
|
|---|
| 13905 | void testFixed32() throws IOException {
|
|---|
| 13906 | - ProtoBufParser parser = new ProtoBufParser(ProtoBufTest.toByteArray(new int[] {0x0d, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 13907 | - ProtoBufRecord thirtyTwoBit = new ProtoBufRecord(parser);
|
|---|
| 13908 | + ProtobufParser parser = new ProtobufParser(ProtoBufTest.toByteArray(new int[] {0x0d, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 13909 | + ProtobufRecord thirtyTwoBit = new ProtobufRecord(parser);
|
|---|
| 13910 | assertEquals(WireType.THIRTY_TWO_BIT, thirtyTwoBit.getType());
|
|---|
| 13911 | assertEquals(1f, thirtyTwoBit.asFloat());
|
|---|
| 13912 | }
|
|---|
| 13913 |
|
|---|
| 13914 | @Test
|
|---|
| 13915 | void testUnknown() throws IOException {
|
|---|
| 13916 | - ProtoBufParser parser = new ProtoBufParser(ProtoBufTest.toByteArray(new int[] {0x0f, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 13917 | - ProtoBufRecord unknown = new ProtoBufRecord(parser);
|
|---|
| 13918 | + ProtobufParser parser = new ProtobufParser(ProtoBufTest.toByteArray(new int[] {0x0f, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 13919 | + ProtobufRecord unknown = new ProtobufRecord(parser);
|
|---|
| 13920 | assertEquals(WireType.UNKNOWN, unknown.getType());
|
|---|
| 13921 | assertEquals(0, unknown.getBytes().length);
|
|---|
| 13922 | }
|
|---|
| 13923 | --
|
|---|
| 13924 | GitLab
|
|---|
| 13925 |
|
|---|
| 13926 |
|
|---|
| 13927 | From 451f7da6f1ebe81be20a7055b0f91635ed35173d Mon Sep 17 00:00:00 2001
|
|---|
| 13928 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 13929 | Date: Mon, 3 May 2021 07:34:45 -0600
|
|---|
| 13930 | Subject: [PATCH 33/50] Vector Primitives: Return immutable bboxes
|
|---|
| 13931 |
|
|---|
| 13932 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 13933 | ---
|
|---|
| 13934 | src/org/openstreetmap/josm/data/vector/VectorNode.java | 2 +-
|
|---|
| 13935 | .../openstreetmap/josm/data/vector/VectorRelation.java | 9 +++++----
|
|---|
| 13936 | src/org/openstreetmap/josm/data/vector/VectorWay.java | 9 +++++----
|
|---|
| 13937 | 3 files changed, 11 insertions(+), 9 deletions(-)
|
|---|
| 13938 |
|
|---|
| 13939 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorNode.java b/src/org/openstreetmap/josm/data/vector/VectorNode.java
|
|---|
| 13940 | index 60aecd8ff..07e9e512f 100644
|
|---|
| 13941 | --- a/src/org/openstreetmap/josm/data/vector/VectorNode.java
|
|---|
| 13942 | +++ b/src/org/openstreetmap/josm/data/vector/VectorNode.java
|
|---|
| 13943 | @@ -103,7 +103,7 @@ public class VectorNode extends VectorPrimitive implements INode {
|
|---|
| 13944 |
|
|---|
| 13945 | @Override
|
|---|
| 13946 | public BBox getBBox() {
|
|---|
| 13947 | - return new BBox(this.lon, this.lat);
|
|---|
| 13948 | + return new BBox(this.lon, this.lat).toImmutable();
|
|---|
| 13949 | }
|
|---|
| 13950 |
|
|---|
| 13951 | @Override
|
|---|
| 13952 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorRelation.java b/src/org/openstreetmap/josm/data/vector/VectorRelation.java
|
|---|
| 13953 | index 0deb57e57..bf30365a9 100644
|
|---|
| 13954 | --- a/src/org/openstreetmap/josm/data/vector/VectorRelation.java
|
|---|
| 13955 | +++ b/src/org/openstreetmap/josm/data/vector/VectorRelation.java
|
|---|
| 13956 | @@ -43,13 +43,14 @@ public class VectorRelation extends VectorPrimitive implements IRelation<VectorR
|
|---|
| 13957 |
|
|---|
| 13958 | @Override
|
|---|
| 13959 | public BBox getBBox() {
|
|---|
| 13960 | - if (cachedBBox == null) {
|
|---|
| 13961 | - cachedBBox = new BBox();
|
|---|
| 13962 | + if (this.cachedBBox == null) {
|
|---|
| 13963 | + BBox tBBox = new BBox();
|
|---|
| 13964 | for (IPrimitive member : this.getMemberPrimitivesList()) {
|
|---|
| 13965 | - cachedBBox.add(member.getBBox());
|
|---|
| 13966 | + tBBox.add(member.getBBox());
|
|---|
| 13967 | }
|
|---|
| 13968 | + this.cachedBBox = tBBox.toImmutable();
|
|---|
| 13969 | }
|
|---|
| 13970 | - return cachedBBox;
|
|---|
| 13971 | + return this.cachedBBox;
|
|---|
| 13972 | }
|
|---|
| 13973 |
|
|---|
| 13974 | protected void addRelationMember(VectorRelationMember member) {
|
|---|
| 13975 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorWay.java b/src/org/openstreetmap/josm/data/vector/VectorWay.java
|
|---|
| 13976 | index 582fca2d4..98fe9aa22 100644
|
|---|
| 13977 | --- a/src/org/openstreetmap/josm/data/vector/VectorWay.java
|
|---|
| 13978 | +++ b/src/org/openstreetmap/josm/data/vector/VectorWay.java
|
|---|
| 13979 | @@ -44,13 +44,14 @@ public class VectorWay extends VectorPrimitive implements IWay<VectorNode> {
|
|---|
| 13980 |
|
|---|
| 13981 | @Override
|
|---|
| 13982 | public BBox getBBox() {
|
|---|
| 13983 | - if (cachedBBox == null) {
|
|---|
| 13984 | - cachedBBox = new BBox();
|
|---|
| 13985 | + if (this.cachedBBox == null) {
|
|---|
| 13986 | + BBox tBBox = new BBox();
|
|---|
| 13987 | for (INode node : this.getNodes()) {
|
|---|
| 13988 | - cachedBBox.add(node.getBBox());
|
|---|
| 13989 | + tBBox.add(node.getBBox());
|
|---|
| 13990 | }
|
|---|
| 13991 | + this.cachedBBox = tBBox.toImmutable();
|
|---|
| 13992 | }
|
|---|
| 13993 | - return cachedBBox;
|
|---|
| 13994 | + return this.cachedBBox;
|
|---|
| 13995 | }
|
|---|
| 13996 |
|
|---|
| 13997 | @Override
|
|---|
| 13998 | --
|
|---|
| 13999 | GitLab
|
|---|
| 14000 |
|
|---|
| 14001 |
|
|---|
| 14002 | From 6856853d21541edf436d11683590dfd84f6fe08e Mon Sep 17 00:00:00 2001
|
|---|
| 14003 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14004 | Date: Mon, 3 May 2021 07:49:21 -0600
|
|---|
| 14005 | Subject: [PATCH 34/50] VectorTile Style: Layers: Use StyleKeys for common
|
|---|
| 14006 | constants
|
|---|
| 14007 |
|
|---|
| 14008 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14009 | ---
|
|---|
| 14010 | .../vectortile/mapbox/style/Layers.java | 51 ++++++++++---------
|
|---|
| 14011 | 1 file changed, 27 insertions(+), 24 deletions(-)
|
|---|
| 14012 |
|
|---|
| 14013 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14014 | index d6e55972a..b76389cf8 100644
|
|---|
| 14015 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14016 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14017 | @@ -1,6 +1,8 @@
|
|---|
| 14018 | // License: GPL. For details, see LICENSE file.
|
|---|
| 14019 | package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style;
|
|---|
| 14020 |
|
|---|
| 14021 | +import org.openstreetmap.josm.gui.mappaint.StyleKeys;
|
|---|
| 14022 | +
|
|---|
| 14023 | import java.awt.Font;
|
|---|
| 14024 | import java.awt.GraphicsEnvironment;
|
|---|
| 14025 | import java.text.MessageFormat;
|
|---|
| 14026 | @@ -58,6 +60,7 @@ public class Layers {
|
|---|
| 14027 | private static final String EMPTY_STRING = "";
|
|---|
| 14028 | private static final char SEMI_COLON = ';';
|
|---|
| 14029 | private static final Pattern CURLY_BRACES = Pattern.compile("(\\{(.*?)})");
|
|---|
| 14030 | + private static final String PAINT = "paint";
|
|---|
| 14031 |
|
|---|
| 14032 | /** A required unique layer name */
|
|---|
| 14033 | private final String id;
|
|---|
| 14034 | @@ -110,8 +113,8 @@ public class Layers {
|
|---|
| 14035 | } else {
|
|---|
| 14036 | this.source = layerInfo.getString("source");
|
|---|
| 14037 | }
|
|---|
| 14038 | - if (layerInfo.containsKey("paint") && layerInfo.get("paint").getValueType() == JsonValue.ValueType.OBJECT) {
|
|---|
| 14039 | - final JsonObject paintObject = layerInfo.getJsonObject("paint");
|
|---|
| 14040 | + if (layerInfo.containsKey(PAINT) && layerInfo.get(PAINT).getValueType() == JsonValue.ValueType.OBJECT) {
|
|---|
| 14041 | + final JsonObject paintObject = layerInfo.getJsonObject(PAINT);
|
|---|
| 14042 | final JsonObject layoutObject = layerInfo.getOrDefault("layout", JsonValue.EMPTY_JSON_OBJECT).asJsonObject();
|
|---|
| 14043 | // Don't throw exceptions here, since we may just point at the styling
|
|---|
| 14044 | if ("visible".equalsIgnoreCase(layoutObject.getString("visibility", "visible"))) {
|
|---|
| 14045 | @@ -177,15 +180,15 @@ public class Layers {
|
|---|
| 14046 | // line-blur, default 0 (px)
|
|---|
| 14047 | // line-color, default #000000, disabled by line-pattern
|
|---|
| 14048 | final String color = paintObject.getString("line-color", "#000000");
|
|---|
| 14049 | - sb.append("color:").append(color).append(SEMI_COLON);
|
|---|
| 14050 | + sb.append(StyleKeys.COLOR).append(':').append(color).append(SEMI_COLON);
|
|---|
| 14051 | // line-opacity, default 1 (0-1)
|
|---|
| 14052 | final JsonNumber opacity = paintObject.getJsonNumber("line-opacity");
|
|---|
| 14053 | if (opacity != null) {
|
|---|
| 14054 | - sb.append("opacity:").append(opacity.numberValue().doubleValue()).append(SEMI_COLON);
|
|---|
| 14055 | + sb.append(StyleKeys.OPACITY).append(':').append(opacity.numberValue().doubleValue()).append(SEMI_COLON);
|
|---|
| 14056 | }
|
|---|
| 14057 | // line-cap, default butt (butt|round|square)
|
|---|
| 14058 | final String cap = layoutObject.getString("line-cap", "butt");
|
|---|
| 14059 | - sb.append("linecap:");
|
|---|
| 14060 | + sb.append(StyleKeys.LINECAP).append(':');
|
|---|
| 14061 | switch (cap) {
|
|---|
| 14062 | case "round":
|
|---|
| 14063 | case "square":
|
|---|
| 14064 | @@ -200,7 +203,7 @@ public class Layers {
|
|---|
| 14065 | // line-dasharray, array of number >= 0, units in line widths, disabled by line-pattern
|
|---|
| 14066 | if (paintObject.containsKey("line-dasharray")) {
|
|---|
| 14067 | final JsonArray dashArray = paintObject.getJsonArray("line-dasharray");
|
|---|
| 14068 | - sb.append("dashes:");
|
|---|
| 14069 | + sb.append(StyleKeys.DASHES).append(':');
|
|---|
| 14070 | sb.append(dashArray.stream().filter(JsonNumber.class::isInstance).map(JsonNumber.class::cast)
|
|---|
| 14071 | .map(JsonNumber::toString).collect(Collectors.joining(",")));
|
|---|
| 14072 | sb.append(SEMI_COLON);
|
|---|
| 14073 | @@ -217,7 +220,7 @@ public class Layers {
|
|---|
| 14074 | // line-translate-anchor
|
|---|
| 14075 | // line-width
|
|---|
| 14076 | final JsonNumber width = paintObject.getJsonNumber("line-width");
|
|---|
| 14077 | - sb.append("width:").append(width == null ? 1 : width.toString()).append(SEMI_COLON);
|
|---|
| 14078 | + sb.append(StyleKeys.WIDTH).append(':').append(width == null ? 1 : width.toString()).append(SEMI_COLON);
|
|---|
| 14079 | return sb.toString();
|
|---|
| 14080 | }
|
|---|
| 14081 |
|
|---|
| 14082 | @@ -341,12 +344,12 @@ public class Layers {
|
|---|
| 14083 | // text-allow-overlap
|
|---|
| 14084 | // text-anchor
|
|---|
| 14085 | // text-color
|
|---|
| 14086 | - if (paintObject.containsKey("text-color")) {
|
|---|
| 14087 | - sb.append("text-color:").append(paintObject.getString("text-color")).append(SEMI_COLON);
|
|---|
| 14088 | + if (paintObject.containsKey(StyleKeys.TEXT_COLOR)) {
|
|---|
| 14089 | + sb.append(StyleKeys.TEXT_COLOR).append(':').append(paintObject.getString(StyleKeys.TEXT_COLOR)).append(SEMI_COLON);
|
|---|
| 14090 | }
|
|---|
| 14091 | // text-field
|
|---|
| 14092 | if (layoutObject.containsKey("text-field")) {
|
|---|
| 14093 | - sb.append("text:")
|
|---|
| 14094 | + sb.append(StyleKeys.TEXT).append(':')
|
|---|
| 14095 | .append(layoutObject.getString("text-field").replace("}", EMPTY_STRING).replace("{", EMPTY_STRING))
|
|---|
| 14096 | .append(SEMI_COLON);
|
|---|
| 14097 | }
|
|---|
| 14098 | @@ -365,9 +368,9 @@ public class Layers {
|
|---|
| 14099 | .orElseGet(() -> fontMatches.stream().filter(font -> font.getPSName().equals(fontString)).findAny()
|
|---|
| 14100 | .orElseGet(() -> fontMatches.stream().filter(font -> font.getFamily().equals(fontString)).findAny().orElse(null))));
|
|---|
| 14101 | if (setFont != null) {
|
|---|
| 14102 | - sb.append("font-family:\"").append(setFont.getFamily()).append('"').append(SEMI_COLON);
|
|---|
| 14103 | - sb.append("font-weight:").append(setFont.isBold() ? "bold" : "normal").append(SEMI_COLON);
|
|---|
| 14104 | - sb.append("font-style:").append(setFont.isItalic() ? "italic" : "normal").append(SEMI_COLON);
|
|---|
| 14105 | + sb.append(StyleKeys.FONT_FAMILY).append(':').append('"').append(setFont.getFamily()).append('"').append(SEMI_COLON);
|
|---|
| 14106 | + sb.append(StyleKeys.FONT_WEIGHT).append(':').append(setFont.isBold() ? "bold" : "normal").append(SEMI_COLON);
|
|---|
| 14107 | + sb.append(StyleKeys.FONT_STYLE).append(':').append(setFont.isItalic() ? "italic" : "normal").append(SEMI_COLON);
|
|---|
| 14108 | break;
|
|---|
| 14109 | }
|
|---|
| 14110 | }
|
|---|
| 14111 | @@ -375,12 +378,12 @@ public class Layers {
|
|---|
| 14112 | }
|
|---|
| 14113 | // text-halo-blur
|
|---|
| 14114 | // text-halo-color
|
|---|
| 14115 | - if (paintObject.containsKey("text-halo-color")) {
|
|---|
| 14116 | - sb.append("text-halo-color:").append(paintObject.getString("text-halo-color")).append(SEMI_COLON);
|
|---|
| 14117 | + if (paintObject.containsKey(StyleKeys.TEXT_HALO_COLOR)) {
|
|---|
| 14118 | + sb.append(StyleKeys.TEXT_HALO_COLOR).append(':').append(paintObject.getString(StyleKeys.TEXT_HALO_COLOR)).append(SEMI_COLON);
|
|---|
| 14119 | }
|
|---|
| 14120 | // text-halo-width
|
|---|
| 14121 | if (paintObject.containsKey("text-halo-width")) {
|
|---|
| 14122 | - sb.append("text-halo-radius:").append(paintObject.getJsonNumber("text-halo-width").intValue()).append(SEMI_COLON);
|
|---|
| 14123 | + sb.append(StyleKeys.TEXT_HALO_RADIUS).append(':').append(2 * paintObject.getJsonNumber("text-halo-width").intValue()).append(SEMI_COLON);
|
|---|
| 14124 | }
|
|---|
| 14125 | // text-ignore-placement
|
|---|
| 14126 | // text-justify
|
|---|
| 14127 | @@ -391,8 +394,8 @@ public class Layers {
|
|---|
| 14128 | // text-max-width
|
|---|
| 14129 | // text-offset
|
|---|
| 14130 | // text-opacity
|
|---|
| 14131 | - if (paintObject.containsKey("text-opacity")) {
|
|---|
| 14132 | - sb.append("text-opacity:").append(paintObject.getJsonNumber("text-opacity").doubleValue()).append(SEMI_COLON);
|
|---|
| 14133 | + if (paintObject.containsKey(StyleKeys.TEXT_OPACITY)) {
|
|---|
| 14134 | + sb.append(StyleKeys.TEXT_OPACITY).append(':').append(paintObject.getJsonNumber(StyleKeys.TEXT_OPACITY).doubleValue()).append(SEMI_COLON);
|
|---|
| 14135 | }
|
|---|
| 14136 | // text-optional
|
|---|
| 14137 | // text-padding
|
|---|
| 14138 | @@ -402,7 +405,7 @@ public class Layers {
|
|---|
| 14139 | // text-rotation-alignment
|
|---|
| 14140 | // text-size
|
|---|
| 14141 | final JsonNumber textSize = layoutObject.getJsonNumber("text-size");
|
|---|
| 14142 | - sb.append("font-size:").append(textSize != null ? textSize.numberValue().toString() : "16").append(SEMI_COLON);
|
|---|
| 14143 | + sb.append(StyleKeys.FONT_SIZE).append(':').append(textSize != null ? textSize.numberValue().toString() : "16").append(SEMI_COLON);
|
|---|
| 14144 | // text-transform
|
|---|
| 14145 | // text-translate
|
|---|
| 14146 | // text-translate-anchor
|
|---|
| 14147 | @@ -416,7 +419,7 @@ public class Layers {
|
|---|
| 14148 | // background-color
|
|---|
| 14149 | final String bgColor = paintObject.getString("background-color", null);
|
|---|
| 14150 | if (bgColor != null) {
|
|---|
| 14151 | - sb.append("fill-color:").append(bgColor).append(SEMI_COLON);
|
|---|
| 14152 | + sb.append(StyleKeys.FILL_COLOR).append(':').append(bgColor).append(SEMI_COLON);
|
|---|
| 14153 | }
|
|---|
| 14154 | // background-opacity
|
|---|
| 14155 | // background-pattern
|
|---|
| 14156 | @@ -427,12 +430,12 @@ public class Layers {
|
|---|
| 14157 | StringBuilder sb = new StringBuilder(50)
|
|---|
| 14158 | // fill-antialias
|
|---|
| 14159 | // fill-color
|
|---|
| 14160 | - .append("fill-color:").append(paintObject.getString("fill-color", "#000000")).append(SEMI_COLON);
|
|---|
| 14161 | + .append(StyleKeys.FILL_COLOR).append(':').append(paintObject.getString(StyleKeys.FILL_COLOR, "#000000")).append(SEMI_COLON);
|
|---|
| 14162 | // fill-opacity
|
|---|
| 14163 | - final JsonNumber opacity = paintObject.getJsonNumber("fill-opacity");
|
|---|
| 14164 | - sb.append("fill-opacity:").append(opacity != null ? opacity.numberValue().toString() : "1").append(SEMI_COLON)
|
|---|
| 14165 | + final JsonNumber opacity = paintObject.getJsonNumber(StyleKeys.FILL_OPACITY);
|
|---|
| 14166 | + sb.append(StyleKeys.FILL_OPACITY).append(':').append(opacity != null ? opacity.numberValue().toString() : "1").append(SEMI_COLON)
|
|---|
| 14167 | // fill-outline-color
|
|---|
| 14168 | - .append("color:").append(paintObject.getString("fill-outline-color",
|
|---|
| 14169 | + .append(StyleKeys.COLOR).append(':').append(paintObject.getString("fill-outline-color",
|
|---|
| 14170 | paintObject.getString("fill-color", "#000000"))).append(SEMI_COLON);
|
|---|
| 14171 | // fill-pattern
|
|---|
| 14172 | // fill-sort-key
|
|---|
| 14173 | --
|
|---|
| 14174 | GitLab
|
|---|
| 14175 |
|
|---|
| 14176 |
|
|---|
| 14177 | From 6241b4fe6c0530ea855655551da194c16401344b Mon Sep 17 00:00:00 2001
|
|---|
| 14178 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14179 | Date: Mon, 3 May 2021 08:14:14 -0600
|
|---|
| 14180 | Subject: [PATCH 35/50] VectorDataSet: Remove cast in getPrimitives
|
|---|
| 14181 |
|
|---|
| 14182 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14183 | ---
|
|---|
| 14184 | .../openstreetmap/josm/data/vector/VectorDataSet.java | 11 +++++------
|
|---|
| 14185 | 1 file changed, 5 insertions(+), 6 deletions(-)
|
|---|
| 14186 |
|
|---|
| 14187 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 14188 | index 1541d169b..9ea4aaa73 100644
|
|---|
| 14189 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 14190 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 14191 | @@ -238,18 +238,17 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 14192 | }
|
|---|
| 14193 |
|
|---|
| 14194 | @Override
|
|---|
| 14195 | - public <T extends VectorPrimitive> Collection<T> getPrimitives(
|
|---|
| 14196 | - Predicate<? super VectorPrimitive> predicate) {
|
|---|
| 14197 | - return tryRead(this.readWriteLock, () -> {
|
|---|
| 14198 | + public <T extends VectorPrimitive> Collection<T> getPrimitives(Predicate<? super VectorPrimitive> predicate) {
|
|---|
| 14199 | + Collection<VectorPrimitive> primitives = tryRead(this.readWriteLock, () -> {
|
|---|
| 14200 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 14201 | if (dataStore == null) {
|
|---|
| 14202 | return null;
|
|---|
| 14203 | }
|
|---|
| 14204 | + return dataStore.stream().map(MVTTile::getData)
|
|---|
| 14205 | + .map(VectorDataStore::getAllPrimitives).flatMap(Collection::stream).distinct().collect(Collectors.toList());
|
|---|
| 14206 |
|
|---|
| 14207 | - // This cast is needed (otherwise, Collections.emptyList doesn't compile)
|
|---|
| 14208 | - return (Collection<T>) new SubclassFilteredCollection<>(dataStore.stream().map(MVTTile::getData)
|
|---|
| 14209 | - .map(VectorDataStore::getAllPrimitives).flatMap(Collection::stream).distinct().collect(Collectors.toList()), predicate);
|
|---|
| 14210 | }).orElseGet(Collections::emptyList);
|
|---|
| 14211 | + return new SubclassFilteredCollection<>(primitives, predicate);
|
|---|
| 14212 | }
|
|---|
| 14213 |
|
|---|
| 14214 | @Override
|
|---|
| 14215 | --
|
|---|
| 14216 | GitLab
|
|---|
| 14217 |
|
|---|
| 14218 |
|
|---|
| 14219 | From 5d2e8ae0d89e0a2c21ac76a9eee5fbe5bc09985d Mon Sep 17 00:00:00 2001
|
|---|
| 14220 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14221 | Date: Mon, 3 May 2021 08:42:19 -0600
|
|---|
| 14222 | Subject: [PATCH 36/50] FIXUP: Line length and some IDE issues
|
|---|
| 14223 |
|
|---|
| 14224 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14225 | ---
|
|---|
| 14226 | .../josm/data/cache/JCSCachedTileLoaderJob.java | 1 -
|
|---|
| 14227 | .../josm/data/imagery/vectortile/mapbox/style/Layers.java | 6 ++++--
|
|---|
| 14228 | .../openstreetmap/josm/data/vector/VectorPrimitive.java | 7 +++----
|
|---|
| 14229 | 3 files changed, 7 insertions(+), 7 deletions(-)
|
|---|
| 14230 |
|
|---|
| 14231 | diff --git a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 14232 | index d07dff6d1..f8603239a 100644
|
|---|
| 14233 | --- a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 14234 | +++ b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
|
|---|
| 14235 | @@ -28,7 +28,6 @@ import org.openstreetmap.josm.tools.HttpClient;
|
|---|
| 14236 | import org.openstreetmap.josm.tools.Logging;
|
|---|
| 14237 | import org.openstreetmap.josm.tools.Utils;
|
|---|
| 14238 |
|
|---|
| 14239 | -import org.apache.commons.compress.utils.IOUtils;
|
|---|
| 14240 | import org.apache.commons.jcs3.access.behavior.ICacheAccess;
|
|---|
| 14241 | import org.apache.commons.jcs3.engine.behavior.ICacheElement;
|
|---|
| 14242 |
|
|---|
| 14243 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14244 | index b76389cf8..ef6bae625 100644
|
|---|
| 14245 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14246 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14247 | @@ -383,7 +383,8 @@ public class Layers {
|
|---|
| 14248 | }
|
|---|
| 14249 | // text-halo-width
|
|---|
| 14250 | if (paintObject.containsKey("text-halo-width")) {
|
|---|
| 14251 | - sb.append(StyleKeys.TEXT_HALO_RADIUS).append(':').append(2 * paintObject.getJsonNumber("text-halo-width").intValue()).append(SEMI_COLON);
|
|---|
| 14252 | + sb.append(StyleKeys.TEXT_HALO_RADIUS).append(':').append(2 * paintObject.getJsonNumber("text-halo-width").intValue())
|
|---|
| 14253 | + .append(SEMI_COLON);
|
|---|
| 14254 | }
|
|---|
| 14255 | // text-ignore-placement
|
|---|
| 14256 | // text-justify
|
|---|
| 14257 | @@ -395,7 +396,8 @@ public class Layers {
|
|---|
| 14258 | // text-offset
|
|---|
| 14259 | // text-opacity
|
|---|
| 14260 | if (paintObject.containsKey(StyleKeys.TEXT_OPACITY)) {
|
|---|
| 14261 | - sb.append(StyleKeys.TEXT_OPACITY).append(':').append(paintObject.getJsonNumber(StyleKeys.TEXT_OPACITY).doubleValue()).append(SEMI_COLON);
|
|---|
| 14262 | + sb.append(StyleKeys.TEXT_OPACITY).append(':').append(paintObject.getJsonNumber(StyleKeys.TEXT_OPACITY).doubleValue())
|
|---|
| 14263 | + .append(SEMI_COLON);
|
|---|
| 14264 | }
|
|---|
| 14265 | // text-optional
|
|---|
| 14266 | // text-padding
|
|---|
| 14267 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java b/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 14268 | index ed9c93937..114b61730 100644
|
|---|
| 14269 | --- a/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 14270 | +++ b/src/org/openstreetmap/josm/data/vector/VectorPrimitive.java
|
|---|
| 14271 | @@ -67,17 +67,16 @@ public abstract class VectorPrimitive extends AbstractPrimitive implements DataL
|
|---|
| 14272 |
|
|---|
| 14273 | @Override
|
|---|
| 14274 | public VectorDataSet getDataSet() {
|
|---|
| 14275 | - return this.dataSet;
|
|---|
| 14276 | + return dataSet;
|
|---|
| 14277 | }
|
|---|
| 14278 |
|
|---|
| 14279 | - protected void setDataSet(VectorDataSet dataSet) {
|
|---|
| 14280 | - this.dataSet = dataSet;
|
|---|
| 14281 | + protected void setDataSet(VectorDataSet newDataSet) {
|
|---|
| 14282 | + dataSet = newDataSet;
|
|---|
| 14283 | }
|
|---|
| 14284 |
|
|---|
| 14285 | /*----------
|
|---|
| 14286 | * MAPPAINT
|
|---|
| 14287 | *--------*/
|
|---|
| 14288 | - private short mappaintCacheIdx;
|
|---|
| 14289 |
|
|---|
| 14290 | @Override
|
|---|
| 14291 | public final StyleCache getCachedStyle() {
|
|---|
| 14292 | --
|
|---|
| 14293 | GitLab
|
|---|
| 14294 |
|
|---|
| 14295 |
|
|---|
| 14296 | From 6314c7c5bccaf2f9b6a6f20ed95a15c9cf1a9602 Mon Sep 17 00:00:00 2001
|
|---|
| 14297 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14298 | Date: Tue, 4 May 2021 09:50:02 -0600
|
|---|
| 14299 | Subject: [PATCH 37/50] FilterWorker: Make it generic
|
|---|
| 14300 |
|
|---|
| 14301 | Currently, Filters do not appear to be used in any plugin (sans
|
|---|
| 14302 | Mapillary). This means that now is a good time to generify the filters,
|
|---|
| 14303 | as we add VectorPrimitives, which we will eventually want to filter.
|
|---|
| 14304 |
|
|---|
| 14305 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14306 | ---
|
|---|
| 14307 | .../josm/data/osm/AbstractPrimitive.java | 50 ++++++++++++++++-
|
|---|
| 14308 | .../josm/data/osm/FilterMatcher.java | 42 ++++++++-------
|
|---|
| 14309 | .../josm/data/osm/FilterWorker.java | 31 ++++++-----
|
|---|
| 14310 | .../josm/data/osm/IFilterablePrimitive.java | 51 ++++++++++++++++++
|
|---|
| 14311 | .../josm/data/osm/OsmPrimitive.java | 53 ++-----------------
|
|---|
| 14312 | 5 files changed, 146 insertions(+), 81 deletions(-)
|
|---|
| 14313 | create mode 100644 src/org/openstreetmap/josm/data/osm/IFilterablePrimitive.java
|
|---|
| 14314 |
|
|---|
| 14315 | diff --git a/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java b/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
|
|---|
| 14316 | index e4ffa73e0..c7da8ecc3 100644
|
|---|
| 14317 | --- a/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
|
|---|
| 14318 | +++ b/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
|
|---|
| 14319 | @@ -31,7 +31,7 @@ import org.openstreetmap.josm.tools.Utils;
|
|---|
| 14320 | *
|
|---|
| 14321 | * @since 4099
|
|---|
| 14322 | */
|
|---|
| 14323 | -public abstract class AbstractPrimitive implements IPrimitive {
|
|---|
| 14324 | +public abstract class AbstractPrimitive implements IPrimitive, IFilterablePrimitive {
|
|---|
| 14325 |
|
|---|
| 14326 | /**
|
|---|
| 14327 | * This flag shows, that the properties have been changed by the user
|
|---|
| 14328 | @@ -352,6 +352,18 @@ public abstract class AbstractPrimitive implements IPrimitive {
|
|---|
| 14329 | }
|
|---|
| 14330 | }
|
|---|
| 14331 |
|
|---|
| 14332 | + /**
|
|---|
| 14333 | + * Update flags
|
|---|
| 14334 | + * @param flag The flag to update
|
|---|
| 14335 | + * @param value The value to set
|
|---|
| 14336 | + * @return {@code true} if the flags have changed
|
|---|
| 14337 | + */
|
|---|
| 14338 | + protected boolean updateFlagsChanged(short flag, boolean value) {
|
|---|
| 14339 | + int oldFlags = flags;
|
|---|
| 14340 | + updateFlags(flag, value);
|
|---|
| 14341 | + return oldFlags != flags;
|
|---|
| 14342 | + }
|
|---|
| 14343 | +
|
|---|
| 14344 | @Override
|
|---|
| 14345 | public void setModified(boolean modified) {
|
|---|
| 14346 | updateFlags(FLAG_MODIFIED, modified);
|
|---|
| 14347 | @@ -409,6 +421,42 @@ public abstract class AbstractPrimitive implements IPrimitive {
|
|---|
| 14348 | return (flags & FLAG_INCOMPLETE) != 0;
|
|---|
| 14349 | }
|
|---|
| 14350 |
|
|---|
| 14351 | + @Override
|
|---|
| 14352 | + public boolean getHiddenType() {
|
|---|
| 14353 | + return (flags & FLAG_HIDDEN_TYPE) != 0;
|
|---|
| 14354 | + }
|
|---|
| 14355 | +
|
|---|
| 14356 | + @Override
|
|---|
| 14357 | + public boolean getDisabledType() {
|
|---|
| 14358 | + return (flags & FLAG_DISABLED_TYPE) != 0;
|
|---|
| 14359 | + }
|
|---|
| 14360 | +
|
|---|
| 14361 | + @Override
|
|---|
| 14362 | + public boolean setDisabledState(boolean hidden) {
|
|---|
| 14363 | + // Store as variables to avoid short circuit boolean return
|
|---|
| 14364 | + final boolean flagDisabled = updateFlagsChanged(FLAG_DISABLED, true);
|
|---|
| 14365 | + final boolean flagHideIfDisabled = updateFlagsChanged(FLAG_HIDE_IF_DISABLED, hidden);
|
|---|
| 14366 | + return flagDisabled || flagHideIfDisabled;
|
|---|
| 14367 | + }
|
|---|
| 14368 | +
|
|---|
| 14369 | + @Override
|
|---|
| 14370 | + public boolean unsetDisabledState() {
|
|---|
| 14371 | + // Store as variables to avoid short circuit boolean return
|
|---|
| 14372 | + final boolean flagDisabled = updateFlagsChanged(FLAG_DISABLED, false);
|
|---|
| 14373 | + final boolean flagHideIfDisabled = updateFlagsChanged(FLAG_HIDE_IF_DISABLED, false);
|
|---|
| 14374 | + return flagDisabled || flagHideIfDisabled;
|
|---|
| 14375 | + }
|
|---|
| 14376 | +
|
|---|
| 14377 | + @Override
|
|---|
| 14378 | + public void setDisabledType(boolean isExplicit) {
|
|---|
| 14379 | + updateFlags(FLAG_DISABLED_TYPE, isExplicit);
|
|---|
| 14380 | + }
|
|---|
| 14381 | +
|
|---|
| 14382 | + @Override
|
|---|
| 14383 | + public void setHiddenType(boolean isExplicit) {
|
|---|
| 14384 | + updateFlags(FLAG_HIDDEN_TYPE, isExplicit);
|
|---|
| 14385 | + }
|
|---|
| 14386 | +
|
|---|
| 14387 | protected String getFlagsAsString() {
|
|---|
| 14388 | StringBuilder builder = new StringBuilder();
|
|---|
| 14389 |
|
|---|
| 14390 | diff --git a/src/org/openstreetmap/josm/data/osm/FilterMatcher.java b/src/org/openstreetmap/josm/data/osm/FilterMatcher.java
|
|---|
| 14391 | index a7cb334ee..5cdb25a90 100644
|
|---|
| 14392 | --- a/src/org/openstreetmap/josm/data/osm/FilterMatcher.java
|
|---|
| 14393 | +++ b/src/org/openstreetmap/josm/data/osm/FilterMatcher.java
|
|---|
| 14394 | @@ -161,23 +161,25 @@ public class FilterMatcher {
|
|---|
| 14395 | * @return when hidden is true, returns whether the primitive is hidden
|
|---|
| 14396 | * when hidden is false, returns whether the primitive is disabled or hidden
|
|---|
| 14397 | */
|
|---|
| 14398 | - private static boolean isFiltered(OsmPrimitive primitive, boolean hidden) {
|
|---|
| 14399 | + private static boolean isFiltered(IPrimitive primitive, boolean hidden) {
|
|---|
| 14400 | return hidden ? primitive.isDisabledAndHidden() : primitive.isDisabled();
|
|---|
| 14401 | }
|
|---|
| 14402 |
|
|---|
| 14403 | /**
|
|---|
| 14404 | * Check if primitive is hidden explicitly.
|
|---|
| 14405 | * Only used for ways and relations.
|
|---|
| 14406 | + * @param <T> The primitive type
|
|---|
| 14407 | * @param primitive the primitive to check
|
|---|
| 14408 | * @param hidden the level where the check is performed
|
|---|
| 14409 | * @return true, if at least one non-inverted filter applies to the primitive
|
|---|
| 14410 | */
|
|---|
| 14411 | - private static boolean isFilterExplicit(OsmPrimitive primitive, boolean hidden) {
|
|---|
| 14412 | + private static <T extends IFilterablePrimitive> boolean isFilterExplicit(T primitive, boolean hidden) {
|
|---|
| 14413 | return hidden ? primitive.getHiddenType() : primitive.getDisabledType();
|
|---|
| 14414 | }
|
|---|
| 14415 |
|
|---|
| 14416 | /**
|
|---|
| 14417 | * Check if all parent ways are filtered.
|
|---|
| 14418 | + * @param <T> The primitive type
|
|---|
| 14419 | * @param primitive the primitive to check
|
|---|
| 14420 | * @param hidden parameter that indicates the minimum level of filtering:
|
|---|
| 14421 | * true when objects need to be hidden to count as filtered and
|
|---|
| 14422 | @@ -187,28 +189,28 @@ public class FilterMatcher {
|
|---|
| 14423 | * parameter <code>hidden</code> and
|
|---|
| 14424 | * (c) at least one of the parent ways is explicitly filtered
|
|---|
| 14425 | */
|
|---|
| 14426 | - private static boolean allParentWaysFiltered(OsmPrimitive primitive, boolean hidden) {
|
|---|
| 14427 | - List<OsmPrimitive> refs = primitive.getReferrers();
|
|---|
| 14428 | + private static <T extends IPrimitive & IFilterablePrimitive> boolean allParentWaysFiltered(T primitive, boolean hidden) {
|
|---|
| 14429 | + List<? extends IPrimitive> refs = primitive.getReferrers();
|
|---|
| 14430 | boolean isExplicit = false;
|
|---|
| 14431 | - for (OsmPrimitive p: refs) {
|
|---|
| 14432 | - if (p instanceof Way) {
|
|---|
| 14433 | + for (IPrimitive p: refs) {
|
|---|
| 14434 | + if (p instanceof IWay && p instanceof IFilterablePrimitive) {
|
|---|
| 14435 | if (!isFiltered(p, hidden))
|
|---|
| 14436 | return false;
|
|---|
| 14437 | - isExplicit |= isFilterExplicit(p, hidden);
|
|---|
| 14438 | + isExplicit |= isFilterExplicit((IFilterablePrimitive) p, hidden);
|
|---|
| 14439 | }
|
|---|
| 14440 | }
|
|---|
| 14441 | return isExplicit;
|
|---|
| 14442 | }
|
|---|
| 14443 |
|
|---|
| 14444 | - private static boolean oneParentWayNotFiltered(OsmPrimitive primitive, boolean hidden) {
|
|---|
| 14445 | - return primitive.referrers(Way.class)
|
|---|
| 14446 | + private static boolean oneParentWayNotFiltered(IPrimitive primitive, boolean hidden) {
|
|---|
| 14447 | + return primitive.getReferrers().stream().filter(IWay.class::isInstance).map(IWay.class::cast)
|
|---|
| 14448 | .anyMatch(p -> !isFiltered(p, hidden));
|
|---|
| 14449 | }
|
|---|
| 14450 |
|
|---|
| 14451 | - private static boolean allParentMultipolygonsFiltered(OsmPrimitive primitive, boolean hidden) {
|
|---|
| 14452 | + private static boolean allParentMultipolygonsFiltered(IPrimitive primitive, boolean hidden) {
|
|---|
| 14453 | boolean isExplicit = false;
|
|---|
| 14454 | - for (Relation r : new SubclassFilteredCollection<OsmPrimitive, Relation>(
|
|---|
| 14455 | - primitive.getReferrers(), OsmPrimitive::isMultipolygon)) {
|
|---|
| 14456 | + for (Relation r : new SubclassFilteredCollection<IPrimitive, Relation>(
|
|---|
| 14457 | + primitive.getReferrers(), IPrimitive::isMultipolygon)) {
|
|---|
| 14458 | if (!isFiltered(r, hidden))
|
|---|
| 14459 | return false;
|
|---|
| 14460 | isExplicit |= isFilterExplicit(r, hidden);
|
|---|
| 14461 | @@ -216,12 +218,12 @@ public class FilterMatcher {
|
|---|
| 14462 | return isExplicit;
|
|---|
| 14463 | }
|
|---|
| 14464 |
|
|---|
| 14465 | - private static boolean oneParentMultipolygonNotFiltered(OsmPrimitive primitive, boolean hidden) {
|
|---|
| 14466 | - return new SubclassFilteredCollection<OsmPrimitive, Relation>(primitive.getReferrers(), OsmPrimitive::isMultipolygon).stream()
|
|---|
| 14467 | + private static boolean oneParentMultipolygonNotFiltered(IPrimitive primitive, boolean hidden) {
|
|---|
| 14468 | + return new SubclassFilteredCollection<IPrimitive, IRelation>(primitive.getReferrers(), IPrimitive::isMultipolygon).stream()
|
|---|
| 14469 | .anyMatch(r -> !isFiltered(r, hidden));
|
|---|
| 14470 | }
|
|---|
| 14471 |
|
|---|
| 14472 | - private static FilterType test(List<FilterInfo> filters, OsmPrimitive primitive, boolean hidden) {
|
|---|
| 14473 | + private static <T extends IPrimitive & IFilterablePrimitive> FilterType test(List<FilterInfo> filters, T primitive, boolean hidden) {
|
|---|
| 14474 | if (primitive.isIncomplete() || primitive.isPreserved())
|
|---|
| 14475 | return FilterType.NOT_FILTERED;
|
|---|
| 14476 |
|
|---|
| 14477 | @@ -245,7 +247,7 @@ public class FilterMatcher {
|
|---|
| 14478 | }
|
|---|
| 14479 | }
|
|---|
| 14480 |
|
|---|
| 14481 | - if (primitive instanceof Node) {
|
|---|
| 14482 | + if (primitive instanceof INode) {
|
|---|
| 14483 | if (filtered) {
|
|---|
| 14484 | // If there is a parent way, that is not hidden, we show the
|
|---|
| 14485 | // node anyway, unless there is no non-inverted filter that
|
|---|
| 14486 | @@ -266,7 +268,7 @@ public class FilterMatcher {
|
|---|
| 14487 | else
|
|---|
| 14488 | return FilterType.NOT_FILTERED;
|
|---|
| 14489 | }
|
|---|
| 14490 | - } else if (primitive instanceof Way) {
|
|---|
| 14491 | + } else if (primitive instanceof IWay) {
|
|---|
| 14492 | if (filtered) {
|
|---|
| 14493 | if (explicitlyFiltered)
|
|---|
| 14494 | return FilterType.EXPLICIT;
|
|---|
| 14495 | @@ -295,6 +297,7 @@ public class FilterMatcher {
|
|---|
| 14496 | * Check if primitive is hidden.
|
|---|
| 14497 | * The filter flags for all parent objects must be set correctly, when
|
|---|
| 14498 | * calling this method.
|
|---|
| 14499 | + * @param <T> The primitive type
|
|---|
| 14500 | * @param primitive the primitive
|
|---|
| 14501 | * @return FilterType.NOT_FILTERED when primitive is not hidden;
|
|---|
| 14502 | * FilterType.EXPLICIT when primitive is hidden and there is a non-inverted
|
|---|
| 14503 | @@ -302,7 +305,7 @@ public class FilterMatcher {
|
|---|
| 14504 | * FilterType.PASSIV when primitive is hidden and all filters that apply
|
|---|
| 14505 | * are inverted
|
|---|
| 14506 | */
|
|---|
| 14507 | - public FilterType isHidden(OsmPrimitive primitive) {
|
|---|
| 14508 | + public <T extends IPrimitive & IFilterablePrimitive> FilterType isHidden(T primitive) {
|
|---|
| 14509 | return test(hiddenFilters, primitive, true);
|
|---|
| 14510 | }
|
|---|
| 14511 |
|
|---|
| 14512 | @@ -310,6 +313,7 @@ public class FilterMatcher {
|
|---|
| 14513 | * Check if primitive is disabled.
|
|---|
| 14514 | * The filter flags for all parent objects must be set correctly, when
|
|---|
| 14515 | * calling this method.
|
|---|
| 14516 | + * @param <T> The primitive type
|
|---|
| 14517 | * @param primitive the primitive
|
|---|
| 14518 | * @return FilterType.NOT_FILTERED when primitive is not disabled;
|
|---|
| 14519 | * FilterType.EXPLICIT when primitive is disabled and there is a non-inverted
|
|---|
| 14520 | @@ -317,7 +321,7 @@ public class FilterMatcher {
|
|---|
| 14521 | * FilterType.PASSIV when primitive is disabled and all filters that apply
|
|---|
| 14522 | * are inverted
|
|---|
| 14523 | */
|
|---|
| 14524 | - public FilterType isDisabled(OsmPrimitive primitive) {
|
|---|
| 14525 | + public <T extends IPrimitive & IFilterablePrimitive> FilterType isDisabled(T primitive) {
|
|---|
| 14526 | return test(disabledFilters, primitive, false);
|
|---|
| 14527 | }
|
|---|
| 14528 |
|
|---|
| 14529 | diff --git a/src/org/openstreetmap/josm/data/osm/FilterWorker.java b/src/org/openstreetmap/josm/data/osm/FilterWorker.java
|
|---|
| 14530 | index 6850c47fc..89118bdd0 100644
|
|---|
| 14531 | --- a/src/org/openstreetmap/josm/data/osm/FilterWorker.java
|
|---|
| 14532 | +++ b/src/org/openstreetmap/josm/data/osm/FilterWorker.java
|
|---|
| 14533 | @@ -9,7 +9,7 @@ import org.openstreetmap.josm.data.osm.search.SearchParseError;
|
|---|
| 14534 | import org.openstreetmap.josm.tools.SubclassFilteredCollection;
|
|---|
| 14535 |
|
|---|
| 14536 | /**
|
|---|
| 14537 | - * Class for applying {@link Filter}s to {@link OsmPrimitive}s.
|
|---|
| 14538 | + * Class for applying {@link Filter}s to {@link IPrimitive}s.
|
|---|
| 14539 | *
|
|---|
| 14540 | * Provides a bridge between Filter GUI and the data.
|
|---|
| 14541 | *
|
|---|
| 14542 | @@ -24,37 +24,41 @@ public final class FilterWorker {
|
|---|
| 14543 | /**
|
|---|
| 14544 | * Apply the filters to the primitives of the data set.
|
|---|
| 14545 | *
|
|---|
| 14546 | + * @param <T> The primitive type
|
|---|
| 14547 | * @param all the collection of primitives for that the filter state should be updated
|
|---|
| 14548 | * @param filters the filters
|
|---|
| 14549 | * @return true, if the filter state (normal / disabled / hidden) of any primitive has changed in the process
|
|---|
| 14550 | * @throws SearchParseError if the search expression in a filter cannot be parsed
|
|---|
| 14551 | - * @since 12383
|
|---|
| 14552 | + * @since 12383, xxx (generics)
|
|---|
| 14553 | */
|
|---|
| 14554 | - public static boolean executeFilters(Collection<OsmPrimitive> all, Filter... filters) throws SearchParseError {
|
|---|
| 14555 | + public static <T extends IPrimitive & IFilterablePrimitive> boolean executeFilters(Collection<T> all, Filter... filters)
|
|---|
| 14556 | + throws SearchParseError {
|
|---|
| 14557 | return executeFilters(all, FilterMatcher.of(filters));
|
|---|
| 14558 | }
|
|---|
| 14559 |
|
|---|
| 14560 | /**
|
|---|
| 14561 | * Apply the filters to the primitives of the data set.
|
|---|
| 14562 | *
|
|---|
| 14563 | + * @param <T> The primitive type
|
|---|
| 14564 | * @param all the collection of primitives for that the filter state should be updated
|
|---|
| 14565 | * @param filterMatcher the FilterMatcher
|
|---|
| 14566 | * @return true, if the filter state (normal / disabled / hidden) of any primitive has changed in the process
|
|---|
| 14567 | + * @since xxx (generics)
|
|---|
| 14568 | */
|
|---|
| 14569 | - public static boolean executeFilters(Collection<OsmPrimitive> all, FilterMatcher filterMatcher) {
|
|---|
| 14570 | + public static <T extends IPrimitive & IFilterablePrimitive> boolean executeFilters(Collection<T> all, FilterMatcher filterMatcher) {
|
|---|
| 14571 | boolean changed;
|
|---|
| 14572 | // first relations, then ways and nodes last; this is required to resolve dependencies
|
|---|
| 14573 | - changed = doExecuteFilters(SubclassFilteredCollection.filter(all, Relation.class::isInstance), filterMatcher);
|
|---|
| 14574 | - changed |= doExecuteFilters(SubclassFilteredCollection.filter(all, Way.class::isInstance), filterMatcher);
|
|---|
| 14575 | - changed |= doExecuteFilters(SubclassFilteredCollection.filter(all, Node.class::isInstance), filterMatcher);
|
|---|
| 14576 | + changed = doExecuteFilters(SubclassFilteredCollection.filter(all, IRelation.class::isInstance), filterMatcher);
|
|---|
| 14577 | + changed |= doExecuteFilters(SubclassFilteredCollection.filter(all, IWay.class::isInstance), filterMatcher);
|
|---|
| 14578 | + changed |= doExecuteFilters(SubclassFilteredCollection.filter(all, INode.class::isInstance), filterMatcher);
|
|---|
| 14579 | return changed;
|
|---|
| 14580 | }
|
|---|
| 14581 |
|
|---|
| 14582 | - private static boolean doExecuteFilters(Collection<OsmPrimitive> all, FilterMatcher filterMatcher) {
|
|---|
| 14583 | + private static <T extends IPrimitive & IFilterablePrimitive> boolean doExecuteFilters(Collection<T> all, FilterMatcher filterMatcher) {
|
|---|
| 14584 |
|
|---|
| 14585 | boolean changed = false;
|
|---|
| 14586 |
|
|---|
| 14587 | - for (OsmPrimitive primitive: all) {
|
|---|
| 14588 | + for (T primitive : all) {
|
|---|
| 14589 | FilterType hiddenType = filterMatcher.isHidden(primitive);
|
|---|
| 14590 | if (hiddenType != FilterType.NOT_FILTERED) {
|
|---|
| 14591 | changed |= primitive.setDisabledState(true);
|
|---|
| 14592 | @@ -75,24 +79,27 @@ public final class FilterWorker {
|
|---|
| 14593 | /**
|
|---|
| 14594 | * Apply the filters to a single primitive.
|
|---|
| 14595 | *
|
|---|
| 14596 | + * @param <T> the primitive type
|
|---|
| 14597 | * @param primitive the primitive
|
|---|
| 14598 | * @param filterMatcher the FilterMatcher
|
|---|
| 14599 | * @return true, if the filter state (normal / disabled / hidden)
|
|---|
| 14600 | * of the primitive has changed in the process
|
|---|
| 14601 | + * @since xxx (generics)
|
|---|
| 14602 | */
|
|---|
| 14603 | - public static boolean executeFilters(OsmPrimitive primitive, FilterMatcher filterMatcher) {
|
|---|
| 14604 | + public static <T extends IPrimitive & IFilterablePrimitive> boolean executeFilters(T primitive, FilterMatcher filterMatcher) {
|
|---|
| 14605 | return doExecuteFilters(Collections.singleton(primitive), filterMatcher);
|
|---|
| 14606 | }
|
|---|
| 14607 |
|
|---|
| 14608 | /**
|
|---|
| 14609 | * Clear all filter flags, i.e. turn off filters.
|
|---|
| 14610 | + * @param <T> the primitive type
|
|---|
| 14611 | * @param prims the primitives
|
|---|
| 14612 | * @return true, if the filter state (normal / disabled / hidden) of any primitive has changed in the process
|
|---|
| 14613 | * @since 12388 (signature)
|
|---|
| 14614 | */
|
|---|
| 14615 | - public static boolean clearFilterFlags(Collection<OsmPrimitive> prims) {
|
|---|
| 14616 | + public static <T extends IPrimitive & IFilterablePrimitive> boolean clearFilterFlags(Collection<T> prims) {
|
|---|
| 14617 | boolean changed = false;
|
|---|
| 14618 | - for (OsmPrimitive osm : prims) {
|
|---|
| 14619 | + for (T osm : prims) {
|
|---|
| 14620 | changed |= osm.unsetDisabledState();
|
|---|
| 14621 | }
|
|---|
| 14622 | return changed;
|
|---|
| 14623 | diff --git a/src/org/openstreetmap/josm/data/osm/IFilterablePrimitive.java b/src/org/openstreetmap/josm/data/osm/IFilterablePrimitive.java
|
|---|
| 14624 | new file mode 100644
|
|---|
| 14625 | index 000000000..f8ba55cfc
|
|---|
| 14626 | --- /dev/null
|
|---|
| 14627 | +++ b/src/org/openstreetmap/josm/data/osm/IFilterablePrimitive.java
|
|---|
| 14628 | @@ -0,0 +1,51 @@
|
|---|
| 14629 | +// License: GPL. For details, see LICENSE file.
|
|---|
| 14630 | +package org.openstreetmap.josm.data.osm;
|
|---|
| 14631 | +
|
|---|
| 14632 | +/**
|
|---|
| 14633 | + * An interface used to indicate that a primitive is filterable
|
|---|
| 14634 | + * @author Taylor Smock
|
|---|
| 14635 | + * @since xxx
|
|---|
| 14636 | + */
|
|---|
| 14637 | +public interface IFilterablePrimitive {
|
|---|
| 14638 | + /**
|
|---|
| 14639 | + * Get binary property used internally by the filter mechanism.
|
|---|
| 14640 | + * @return {@code true} if this object has the "hidden type" flag enabled
|
|---|
| 14641 | + */
|
|---|
| 14642 | + boolean getHiddenType();
|
|---|
| 14643 | +
|
|---|
| 14644 | + /**
|
|---|
| 14645 | + * Get binary property used internally by the filter mechanism.
|
|---|
| 14646 | + * @return {@code true} if this object has the "disabled type" flag enabled
|
|---|
| 14647 | + */
|
|---|
| 14648 | + boolean getDisabledType();
|
|---|
| 14649 | +
|
|---|
| 14650 | + /**
|
|---|
| 14651 | + * Make the primitive disabled (e.g. if a filter applies).
|
|---|
| 14652 | + *
|
|---|
| 14653 | + * To enable the primitive again, use unsetDisabledState.
|
|---|
| 14654 | + * @param hidden if the primitive should be completely hidden from view or
|
|---|
| 14655 | + * just shown in gray color.
|
|---|
| 14656 | + * @return true, any flag has changed; false if you try to set the disabled
|
|---|
| 14657 | + * state to the value that is already preset
|
|---|
| 14658 | + */
|
|---|
| 14659 | + boolean setDisabledState(boolean hidden);
|
|---|
| 14660 | +
|
|---|
| 14661 | + /**
|
|---|
| 14662 | + * Remove the disabled flag from the primitive.
|
|---|
| 14663 | + * Afterwards, the primitive is displayed normally and can be selected again.
|
|---|
| 14664 | + * @return {@code true} if a change occurred
|
|---|
| 14665 | + */
|
|---|
| 14666 | + boolean unsetDisabledState();
|
|---|
| 14667 | +
|
|---|
| 14668 | + /**
|
|---|
| 14669 | + * Set binary property used internally by the filter mechanism.
|
|---|
| 14670 | + * @param isExplicit new "disabled type" flag value
|
|---|
| 14671 | + */
|
|---|
| 14672 | + void setDisabledType(boolean isExplicit);
|
|---|
| 14673 | +
|
|---|
| 14674 | + /**
|
|---|
| 14675 | + * Set binary property used internally by the filter mechanism.
|
|---|
| 14676 | + * @param isExplicit new "hidden type" flag value
|
|---|
| 14677 | + */
|
|---|
| 14678 | + void setHiddenType(boolean isExplicit);
|
|---|
| 14679 | +}
|
|---|
| 14680 | diff --git a/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java b/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
|
|---|
| 14681 | index 496751f98..3db783968 100644
|
|---|
| 14682 | --- a/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
|
|---|
| 14683 | +++ b/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
|
|---|
| 14684 | @@ -329,22 +329,11 @@ public abstract class OsmPrimitive extends AbstractPrimitive implements Template
|
|---|
| 14685 | }
|
|---|
| 14686 | }
|
|---|
| 14687 |
|
|---|
| 14688 | - /**
|
|---|
| 14689 | - * Make the primitive disabled (e.g. if a filter applies).
|
|---|
| 14690 | - *
|
|---|
| 14691 | - * To enable the primitive again, use unsetDisabledState.
|
|---|
| 14692 | - * @param hidden if the primitive should be completely hidden from view or
|
|---|
| 14693 | - * just shown in gray color.
|
|---|
| 14694 | - * @return true, any flag has changed; false if you try to set the disabled
|
|---|
| 14695 | - * state to the value that is already preset
|
|---|
| 14696 | - */
|
|---|
| 14697 | + @Override
|
|---|
| 14698 | public boolean setDisabledState(boolean hidden) {
|
|---|
| 14699 | boolean locked = writeLock();
|
|---|
| 14700 | try {
|
|---|
| 14701 | - int oldFlags = flags;
|
|---|
| 14702 | - updateFlagsNoLock(FLAG_DISABLED, true);
|
|---|
| 14703 | - updateFlagsNoLock(FLAG_HIDE_IF_DISABLED, hidden);
|
|---|
| 14704 | - return oldFlags != flags;
|
|---|
| 14705 | + return super.setDisabledState(hidden);
|
|---|
| 14706 | } finally {
|
|---|
| 14707 | writeUnlock(locked);
|
|---|
| 14708 | }
|
|---|
| 14709 | @@ -355,34 +344,16 @@ public abstract class OsmPrimitive extends AbstractPrimitive implements Template
|
|---|
| 14710 | * Afterwards, the primitive is displayed normally and can be selected again.
|
|---|
| 14711 | * @return {@code true} if a change occurred
|
|---|
| 14712 | */
|
|---|
| 14713 | + @Override
|
|---|
| 14714 | public boolean unsetDisabledState() {
|
|---|
| 14715 | boolean locked = writeLock();
|
|---|
| 14716 | try {
|
|---|
| 14717 | - int oldFlags = flags;
|
|---|
| 14718 | - updateFlagsNoLock(FLAG_DISABLED, false);
|
|---|
| 14719 | - updateFlagsNoLock(FLAG_HIDE_IF_DISABLED, false);
|
|---|
| 14720 | - return oldFlags != flags;
|
|---|
| 14721 | + return super.unsetDisabledState();
|
|---|
| 14722 | } finally {
|
|---|
| 14723 | writeUnlock(locked);
|
|---|
| 14724 | }
|
|---|
| 14725 | }
|
|---|
| 14726 |
|
|---|
| 14727 | - /**
|
|---|
| 14728 | - * Set binary property used internally by the filter mechanism.
|
|---|
| 14729 | - * @param isExplicit new "disabled type" flag value
|
|---|
| 14730 | - */
|
|---|
| 14731 | - public void setDisabledType(boolean isExplicit) {
|
|---|
| 14732 | - updateFlags(FLAG_DISABLED_TYPE, isExplicit);
|
|---|
| 14733 | - }
|
|---|
| 14734 | -
|
|---|
| 14735 | - /**
|
|---|
| 14736 | - * Set binary property used internally by the filter mechanism.
|
|---|
| 14737 | - * @param isExplicit new "hidden type" flag value
|
|---|
| 14738 | - */
|
|---|
| 14739 | - public void setHiddenType(boolean isExplicit) {
|
|---|
| 14740 | - updateFlags(FLAG_HIDDEN_TYPE, isExplicit);
|
|---|
| 14741 | - }
|
|---|
| 14742 | -
|
|---|
| 14743 | /**
|
|---|
| 14744 | * Set binary property used internally by the filter mechanism.
|
|---|
| 14745 | * @param isPreserved new "preserved" flag value
|
|---|
| 14746 | @@ -402,22 +373,6 @@ public abstract class OsmPrimitive extends AbstractPrimitive implements Template
|
|---|
| 14747 | return ((flags & FLAG_DISABLED) != 0) && ((flags & FLAG_HIDE_IF_DISABLED) != 0);
|
|---|
| 14748 | }
|
|---|
| 14749 |
|
|---|
| 14750 | - /**
|
|---|
| 14751 | - * Get binary property used internally by the filter mechanism.
|
|---|
| 14752 | - * @return {@code true} if this object has the "hidden type" flag enabled
|
|---|
| 14753 | - */
|
|---|
| 14754 | - public boolean getHiddenType() {
|
|---|
| 14755 | - return (flags & FLAG_HIDDEN_TYPE) != 0;
|
|---|
| 14756 | - }
|
|---|
| 14757 | -
|
|---|
| 14758 | - /**
|
|---|
| 14759 | - * Get binary property used internally by the filter mechanism.
|
|---|
| 14760 | - * @return {@code true} if this object has the "disabled type" flag enabled
|
|---|
| 14761 | - */
|
|---|
| 14762 | - public boolean getDisabledType() {
|
|---|
| 14763 | - return (flags & FLAG_DISABLED_TYPE) != 0;
|
|---|
| 14764 | - }
|
|---|
| 14765 | -
|
|---|
| 14766 | @Override
|
|---|
| 14767 | public boolean isPreserved() {
|
|---|
| 14768 | return (flags & FLAG_PRESERVED) != 0;
|
|---|
| 14769 | --
|
|---|
| 14770 | GitLab
|
|---|
| 14771 |
|
|---|
| 14772 |
|
|---|
| 14773 | From 337febd24fb7be52bd38cd9135815991e8b3d800 Mon Sep 17 00:00:00 2001
|
|---|
| 14774 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14775 | Date: Tue, 4 May 2021 10:02:28 -0600
|
|---|
| 14776 | Subject: [PATCH 38/50] MapboxVectorTileSource: Check that a json has required
|
|---|
| 14777 | keys
|
|---|
| 14778 |
|
|---|
| 14779 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14780 | ---
|
|---|
| 14781 | .../vectortile/mapbox/MapboxVectorTileSource.java | 9 ++++++---
|
|---|
| 14782 | 1 file changed, 6 insertions(+), 3 deletions(-)
|
|---|
| 14783 |
|
|---|
| 14784 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 14785 | index 62647d1bb..18a4911a7 100644
|
|---|
| 14786 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 14787 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSource.java
|
|---|
| 14788 | @@ -10,6 +10,7 @@ import java.util.stream.Collectors;
|
|---|
| 14789 |
|
|---|
| 14790 | import javax.json.Json;
|
|---|
| 14791 | import javax.json.JsonException;
|
|---|
| 14792 | +import javax.json.JsonObject;
|
|---|
| 14793 | import javax.json.JsonReader;
|
|---|
| 14794 |
|
|---|
| 14795 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 14796 | @@ -41,9 +42,11 @@ public class MapboxVectorTileSource extends JosmTemplatedTMSTileSource {
|
|---|
| 14797 | try (CachedFile style = new CachedFile(info.getUrl());
|
|---|
| 14798 | InputStream inputStream = style.getInputStream();
|
|---|
| 14799 | JsonReader reader = Json.createReader(inputStream)) {
|
|---|
| 14800 | - reader.readObject();
|
|---|
| 14801 | - // OK, we have a stylesheet
|
|---|
| 14802 | - mapBoxVectorStyle = MapboxVectorStyle.getMapboxVectorStyle(info.getUrl());
|
|---|
| 14803 | + JsonObject object = reader.readObject();
|
|---|
| 14804 | + // OK, we may have a stylesheet. "version", "layers", and "sources" are all required.
|
|---|
| 14805 | + if (object.containsKey("version") && object.containsKey("layers") && object.containsKey("sources")) {
|
|---|
| 14806 | + mapBoxVectorStyle = MapboxVectorStyle.getMapboxVectorStyle(info.getUrl());
|
|---|
| 14807 | + }
|
|---|
| 14808 | } catch (IOException | JsonException e) {
|
|---|
| 14809 | Logging.trace(e);
|
|---|
| 14810 | }
|
|---|
| 14811 | --
|
|---|
| 14812 | GitLab
|
|---|
| 14813 |
|
|---|
| 14814 |
|
|---|
| 14815 | From b6ea0250e2c114923a218c3641d301092f97680e Mon Sep 17 00:00:00 2001
|
|---|
| 14816 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14817 | Date: Tue, 4 May 2021 10:47:57 -0600
|
|---|
| 14818 | Subject: [PATCH 39/50] Selection Listener: Rename interface methods
|
|---|
| 14819 |
|
|---|
| 14820 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14821 | ---
|
|---|
| 14822 | .../josm/data/osm/event/IDataSelectionEventSource.java | 4 ++--
|
|---|
| 14823 | src/org/openstreetmap/josm/data/vector/VectorDataSet.java | 4 ++--
|
|---|
| 14824 | 2 files changed, 4 insertions(+), 4 deletions(-)
|
|---|
| 14825 |
|
|---|
| 14826 | diff --git a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 14827 | index 4f1d75d18..14fe8eee5 100644
|
|---|
| 14828 | --- a/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 14829 | +++ b/src/org/openstreetmap/josm/data/osm/event/IDataSelectionEventSource.java
|
|---|
| 14830 | @@ -24,12 +24,12 @@ public interface IDataSelectionEventSource<O extends IPrimitive, N extends INode
|
|---|
| 14831 | * @param listener The listener to add
|
|---|
| 14832 | * @return {@code true} if the listener was added
|
|---|
| 14833 | */
|
|---|
| 14834 | - boolean addListener(IDataSelectionListener<O, N, W, R, D> listener);
|
|---|
| 14835 | + boolean addSelectionListener(IDataSelectionListener<O, N, W, R, D> listener);
|
|---|
| 14836 |
|
|---|
| 14837 | /**
|
|---|
| 14838 | * Remove a listener
|
|---|
| 14839 | * @param listener The listener to remove
|
|---|
| 14840 | * @return {@code true} if the listener was removed
|
|---|
| 14841 | */
|
|---|
| 14842 | - boolean removeListener(IDataSelectionListener<O, N, W, R, D> listener);
|
|---|
| 14843 | + boolean removeSelectionListener(IDataSelectionListener<O, N, W, R, D> listener);
|
|---|
| 14844 | }
|
|---|
| 14845 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 14846 | index 9ea4aaa73..b377065af 100644
|
|---|
| 14847 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 14848 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 14849 | @@ -637,7 +637,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 14850 | }
|
|---|
| 14851 |
|
|---|
| 14852 | @Override
|
|---|
| 14853 | - public boolean addListener(IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 14854 | + public boolean addSelectionListener(IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 14855 | if (!this.listeners.containsListener(listener)) {
|
|---|
| 14856 | this.listeners.addListener(listener);
|
|---|
| 14857 | }
|
|---|
| 14858 | @@ -645,7 +645,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 14859 | }
|
|---|
| 14860 |
|
|---|
| 14861 | @Override
|
|---|
| 14862 | - public boolean removeListener(IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 14863 | + public boolean removeSelectionListener(IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 14864 | if (this.listeners.containsListener(listener)) {
|
|---|
| 14865 | this.listeners.removeListener(listener);
|
|---|
| 14866 | }
|
|---|
| 14867 | --
|
|---|
| 14868 | GitLab
|
|---|
| 14869 |
|
|---|
| 14870 |
|
|---|
| 14871 | From 01cf96f513a58e0c9bf429046231b7d34b0e6c09 Mon Sep 17 00:00:00 2001
|
|---|
| 14872 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14873 | Date: Tue, 4 May 2021 13:28:52 -0600
|
|---|
| 14874 | Subject: [PATCH 40/50] Mapbox Style: Fix text-halo-width
|
|---|
| 14875 |
|
|---|
| 14876 | * JOSM equivalent is text-halo-radius, which means text-halo-width
|
|---|
| 14877 | needed to be divided by 2
|
|---|
| 14878 |
|
|---|
| 14879 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14880 | ---
|
|---|
| 14881 | .../josm/data/imagery/vectortile/mapbox/style/Layers.java | 2 +-
|
|---|
| 14882 | .../josm/data/imagery/vectortile/mapbox/style/LayersTest.java | 4 ++--
|
|---|
| 14883 | 2 files changed, 3 insertions(+), 3 deletions(-)
|
|---|
| 14884 |
|
|---|
| 14885 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14886 | index ef6bae625..157e48961 100644
|
|---|
| 14887 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14888 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/Layers.java
|
|---|
| 14889 | @@ -383,7 +383,7 @@ public class Layers {
|
|---|
| 14890 | }
|
|---|
| 14891 | // text-halo-width
|
|---|
| 14892 | if (paintObject.containsKey("text-halo-width")) {
|
|---|
| 14893 | - sb.append(StyleKeys.TEXT_HALO_RADIUS).append(':').append(2 * paintObject.getJsonNumber("text-halo-width").intValue())
|
|---|
| 14894 | + sb.append(StyleKeys.TEXT_HALO_RADIUS).append(':').append(paintObject.getJsonNumber("text-halo-width").intValue() / 2)
|
|---|
| 14895 | .append(SEMI_COLON);
|
|---|
| 14896 | }
|
|---|
| 14897 | // text-ignore-placement
|
|---|
| 14898 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 14899 | index 28b09b950..5db4d5a3d 100644
|
|---|
| 14900 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 14901 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 14902 | @@ -248,7 +248,7 @@ class LayersTest {
|
|---|
| 14903 | assertEquals(Layers.Type.SYMBOL, fullLineLayer.getType());
|
|---|
| 14904 | assertEquals("node::random-layer-id{icon-image:concat(\"random-image\");icon-offset-x:2.0;icon-offset-y:3.0;"
|
|---|
| 14905 | + "icon-opacity:0.5;icon-rotation:30.0;text-color:#fffff0;text:something;font-family:\"SansSerif\";font-weight:normal;"
|
|---|
| 14906 | - + "font-style:normal;text-halo-color:#ffffff;text-halo-radius:16;text-opacity:0.6;font-size:25;}", fullLineLayer.toString());
|
|---|
| 14907 | + + "font-style:normal;text-halo-color:#ffffff;text-halo-radius:8;text-opacity:0.6;font-size:25;}", fullLineLayer.toString());
|
|---|
| 14908 |
|
|---|
| 14909 | // Test an invisible symbol
|
|---|
| 14910 | Layers fullLineInvisibleLayer = new Layers(Json.createObjectBuilder()
|
|---|
| 14911 | @@ -272,7 +272,7 @@ class LayersTest {
|
|---|
| 14912 | .build());
|
|---|
| 14913 | assertEquals("node::random-layer-id{icon-image:concat(tag(\"value\"));icon-offset-x:2.0;icon-offset-y:3.0;"
|
|---|
| 14914 | + "icon-opacity:0.5;icon-rotation:30.0;text-color:#fffff0;text:something;font-family:\"SansSerif\";font-weight:normal;"
|
|---|
| 14915 | - + "font-style:normal;text-halo-color:#ffffff;text-halo-radius:16;text-opacity:0.6;font-size:25;}",
|
|---|
| 14916 | + + "font-style:normal;text-halo-color:#ffffff;text-halo-radius:8;text-opacity:0.6;font-size:25;}",
|
|---|
| 14917 | fullOneIconImagePlaceholderLineLayer.toString());
|
|---|
| 14918 |
|
|---|
| 14919 | // Test with placeholders in icon-image
|
|---|
| 14920 | --
|
|---|
| 14921 | GitLab
|
|---|
| 14922 |
|
|---|
| 14923 |
|
|---|
| 14924 | From b05ac219b7fbaafd891b55924122fce4694bf589 Mon Sep 17 00:00:00 2001
|
|---|
| 14925 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14926 | Date: Tue, 4 May 2021 13:31:00 -0600
|
|---|
| 14927 | Subject: [PATCH 41/50] ProtoBufTest -> ProtobufTest and 2048 -> 4096
|
|---|
| 14928 |
|
|---|
| 14929 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14930 | ---
|
|---|
| 14931 | .../josm/data/protobuf/ProtobufRecordTest.java | 4 ++--
|
|---|
| 14932 | .../data/protobuf/{ProtoBufTest.java => ProtobufTest.java} | 6 +++---
|
|---|
| 14933 | 2 files changed, 5 insertions(+), 5 deletions(-)
|
|---|
| 14934 | rename test/unit/org/openstreetmap/josm/data/protobuf/{ProtoBufTest.java => ProtobufTest.java} (98%)
|
|---|
| 14935 |
|
|---|
| 14936 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufRecordTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufRecordTest.java
|
|---|
| 14937 | index 6573d36fc..f99aa7e2e 100644
|
|---|
| 14938 | --- a/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufRecordTest.java
|
|---|
| 14939 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufRecordTest.java
|
|---|
| 14940 | @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test;
|
|---|
| 14941 | class ProtobufRecordTest {
|
|---|
| 14942 | @Test
|
|---|
| 14943 | void testFixed32() throws IOException {
|
|---|
| 14944 | - ProtobufParser parser = new ProtobufParser(ProtoBufTest.toByteArray(new int[] {0x0d, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 14945 | + ProtobufParser parser = new ProtobufParser(ProtobufTest.toByteArray(new int[] {0x0d, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 14946 | ProtobufRecord thirtyTwoBit = new ProtobufRecord(parser);
|
|---|
| 14947 | assertEquals(WireType.THIRTY_TWO_BIT, thirtyTwoBit.getType());
|
|---|
| 14948 | assertEquals(1f, thirtyTwoBit.asFloat());
|
|---|
| 14949 | @@ -22,7 +22,7 @@ class ProtobufRecordTest {
|
|---|
| 14950 |
|
|---|
| 14951 | @Test
|
|---|
| 14952 | void testUnknown() throws IOException {
|
|---|
| 14953 | - ProtobufParser parser = new ProtobufParser(ProtoBufTest.toByteArray(new int[] {0x0f, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 14954 | + ProtobufParser parser = new ProtobufParser(ProtobufTest.toByteArray(new int[] {0x0f, 0x00, 0x00, 0x80, 0x3f}));
|
|---|
| 14955 | ProtobufRecord unknown = new ProtobufRecord(parser);
|
|---|
| 14956 | assertEquals(WireType.UNKNOWN, unknown.getType());
|
|---|
| 14957 | assertEquals(0, unknown.getBytes().length);
|
|---|
| 14958 | diff --git a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufTest.java
|
|---|
| 14959 | similarity index 98%
|
|---|
| 14960 | rename from test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 14961 | rename to test/unit/org/openstreetmap/josm/data/protobuf/ProtobufTest.java
|
|---|
| 14962 | index e5cd8c738..d6f42cca8 100644
|
|---|
| 14963 | --- a/test/unit/org/openstreetmap/josm/data/protobuf/ProtoBufTest.java
|
|---|
| 14964 | +++ b/test/unit/org/openstreetmap/josm/data/protobuf/ProtobufTest.java
|
|---|
| 14965 | @@ -42,7 +42,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 14966 | * @author Taylor Smock
|
|---|
| 14967 | * @since xxx
|
|---|
| 14968 | */
|
|---|
| 14969 | -class ProtoBufTest {
|
|---|
| 14970 | +class ProtobufTest {
|
|---|
| 14971 | /**
|
|---|
| 14972 | * Convert an int array into a byte array
|
|---|
| 14973 | * @param intArray The int array to convert (NOTE: numbers must be below 255)
|
|---|
| 14974 | @@ -93,8 +93,8 @@ class ProtoBufTest {
|
|---|
| 14975 | Layer mapillaryPictures = layers.get(1);
|
|---|
| 14976 | assertEquals("mapillary-sequences", mapillarySequences.getName());
|
|---|
| 14977 | assertEquals("mapillary-images", mapillaryPictures.getName());
|
|---|
| 14978 | - assertEquals(2048, mapillarySequences.getExtent());
|
|---|
| 14979 | - assertEquals(2048, mapillaryPictures.getExtent());
|
|---|
| 14980 | + assertEquals(4096, mapillarySequences.getExtent());
|
|---|
| 14981 | + assertEquals(4096, mapillaryPictures.getExtent());
|
|---|
| 14982 |
|
|---|
| 14983 | assertEquals(1,
|
|---|
| 14984 | mapillarySequences.getFeatures().stream().filter(feature -> feature.getId() == 233760500).count());
|
|---|
| 14985 | --
|
|---|
| 14986 | GitLab
|
|---|
| 14987 |
|
|---|
| 14988 |
|
|---|
| 14989 | From 88d0f9994728aced4b449bff4db974a143c43edf Mon Sep 17 00:00:00 2001
|
|---|
| 14990 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 14991 | Date: Tue, 4 May 2021 14:10:22 -0600
|
|---|
| 14992 | Subject: [PATCH 42/50] FIXUP: tests were broken due to removal of dedup
|
|---|
| 14993 | functionality
|
|---|
| 14994 |
|
|---|
| 14995 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 14996 | ---
|
|---|
| 14997 | .../josm/data/vector/VectorDataSet.java | 23 +++++++-
|
|---|
| 14998 | .../josm/data/vector/VectorDataSetTest.java | 52 ++++++-------------
|
|---|
| 14999 | 2 files changed, 37 insertions(+), 38 deletions(-)
|
|---|
| 15000 |
|
|---|
| 15001 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15002 | index b377065af..d626156d4 100644
|
|---|
| 15003 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15004 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15005 | @@ -51,6 +51,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15006 | // Note: In Java 8, computeIfAbsent is blocking for both pre-existing and new values. In Java 9, it is only blocking
|
|---|
| 15007 | // for new values (perf increase). See JDK-8161372 for more info.
|
|---|
| 15008 | private final Map<Integer, Storage<MVTTile>> dataStoreMap = new ConcurrentHashMap<>();
|
|---|
| 15009 | + // This is for "custom" data
|
|---|
| 15010 | + private final VectorDataStore customDataStore = new VectorDataStore();
|
|---|
| 15011 | // Both of these listener lists are useless, since they expect OsmPrimitives at this time
|
|---|
| 15012 | private final ListenerList<HighlightUpdateListener> highlightUpdateListenerListenerList = ListenerList.create();
|
|---|
| 15013 | private final ListenerList<DataSelectionListener> dataSelectionListenerListenerList = ListenerList.create();
|
|---|
| 15014 | @@ -134,9 +136,25 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15015 | this.name = name;
|
|---|
| 15016 | }
|
|---|
| 15017 |
|
|---|
| 15018 | + /**
|
|---|
| 15019 | + * Add a primitive to the custom data store
|
|---|
| 15020 | + * @param primitive the primitive to add
|
|---|
| 15021 | + */
|
|---|
| 15022 | @Override
|
|---|
| 15023 | public void addPrimitive(VectorPrimitive primitive) {
|
|---|
| 15024 | - throw new UnsupportedOperationException("Custom vector primitives are not currently supported");
|
|---|
| 15025 | + tryWrite(this.readWriteLock, () -> {
|
|---|
| 15026 | + this.customDataStore.addPrimitive(primitive);
|
|---|
| 15027 | + primitive.setDataSet(this);
|
|---|
| 15028 | + });
|
|---|
| 15029 | + }
|
|---|
| 15030 | +
|
|---|
| 15031 | + /**
|
|---|
| 15032 | + * Remove a primitive from the custom data store
|
|---|
| 15033 | + * @param primitive The primitive to add to the custom data store
|
|---|
| 15034 | + */
|
|---|
| 15035 | + public void removePrimitive(VectorPrimitive primitive) {
|
|---|
| 15036 | + this.customDataStore.removePrimitive(primitive);
|
|---|
| 15037 | + primitive.setDataSet(null);
|
|---|
| 15038 | }
|
|---|
| 15039 |
|
|---|
| 15040 | @Override
|
|---|
| 15041 | @@ -645,7 +663,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15042 | }
|
|---|
| 15043 |
|
|---|
| 15044 | @Override
|
|---|
| 15045 | - public boolean removeSelectionListener(IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 15046 | + public boolean removeSelectionListener(
|
|---|
| 15047 | + IDataSelectionListener<VectorPrimitive, VectorNode, VectorWay, VectorRelation, VectorDataSet> listener) {
|
|---|
| 15048 | if (this.listeners.containsListener(listener)) {
|
|---|
| 15049 | this.listeners.removeListener(listener);
|
|---|
| 15050 | }
|
|---|
| 15051 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 15052 | index 0e7a572d5..c0a1c8532 100644
|
|---|
| 15053 | --- a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 15054 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 15055 | @@ -1,20 +1,11 @@
|
|---|
| 15056 | // License: GPL. For details, see LICENSE file.
|
|---|
| 15057 | package org.openstreetmap.josm.data.vector;
|
|---|
| 15058 |
|
|---|
| 15059 | -import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 15060 | -import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 15061 | -
|
|---|
| 15062 | -
|
|---|
| 15063 | -import java.nio.file.Paths;
|
|---|
| 15064 | -import java.text.MessageFormat;
|
|---|
| 15065 | -import java.util.ArrayList;
|
|---|
| 15066 | -import java.util.Collection;
|
|---|
| 15067 | -import java.util.Collections;
|
|---|
| 15068 | -import java.util.HashSet;
|
|---|
| 15069 | -import java.util.List;
|
|---|
| 15070 | -import java.util.Map;
|
|---|
| 15071 | -import java.util.stream.Collectors;
|
|---|
| 15072 | -
|
|---|
| 15073 | +import org.awaitility.Awaitility;
|
|---|
| 15074 | +import org.awaitility.Durations;
|
|---|
| 15075 | +import org.junit.jupiter.api.BeforeEach;
|
|---|
| 15076 | +import org.junit.jupiter.api.Test;
|
|---|
| 15077 | +import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 15078 | import org.openstreetmap.josm.TestUtils;
|
|---|
| 15079 | import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
|---|
| 15080 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile;
|
|---|
| 15081 | @@ -23,11 +14,14 @@ import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorTileSou
|
|---|
| 15082 | import org.openstreetmap.josm.gui.layer.imagery.MVTLayer;
|
|---|
| 15083 | import org.openstreetmap.josm.testutils.JOSMTestRules;
|
|---|
| 15084 |
|
|---|
| 15085 | -import org.awaitility.Awaitility;
|
|---|
| 15086 | -import org.awaitility.Durations;
|
|---|
| 15087 | -import org.junit.jupiter.api.BeforeEach;
|
|---|
| 15088 | -import org.junit.jupiter.api.Test;
|
|---|
| 15089 | -import org.junit.jupiter.api.extension.RegisterExtension;
|
|---|
| 15090 | +import java.nio.file.Paths;
|
|---|
| 15091 | +import java.util.ArrayList;
|
|---|
| 15092 | +import java.util.Collection;
|
|---|
| 15093 | +import java.util.Collections;
|
|---|
| 15094 | +import java.util.HashSet;
|
|---|
| 15095 | +
|
|---|
| 15096 | +import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 15097 | +import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 15098 |
|
|---|
| 15099 | /**
|
|---|
| 15100 | * A test for {@link VectorDataSet}
|
|---|
| 15101 | @@ -122,24 +116,10 @@ class VectorDataSetTest {
|
|---|
| 15102 | assertEquals(55, dataSet.getNodes().stream().filter(node -> "mapillary-images".equals(node.getLayer())).count());
|
|---|
| 15103 | // Please note that this dataset originally had the <i>same</i> id for all the images
|
|---|
| 15104 | // (MVT v2 explicitly said that ids had to be unique in a layer, MVT v1 did not)
|
|---|
| 15105 | - assertEquals(55, dataSet.getNodes().stream().map(node -> node.get("original_id")).count());
|
|---|
| 15106 | - assertEquals(1, dataSet.getNodes().stream().map(node -> node.get("original_id")).distinct().count());
|
|---|
| 15107 | + // This number is from the 56 nodes - original node with id - single node on mapillary-sequences layer = 54
|
|---|
| 15108 | + assertEquals(54, dataSet.getNodes().stream().filter(node -> node.hasKey("original_id")).count());
|
|---|
| 15109 | + assertEquals(1, dataSet.getNodes().stream().filter(node -> node.hasKey("original_id")).map(node -> node.get("original_id")).distinct().count());
|
|---|
| 15110 | assertEquals(1, dataSet.getWays().size());
|
|---|
| 15111 | assertEquals(0, dataSet.getRelations().size());
|
|---|
| 15112 | }
|
|---|
| 15113 | -
|
|---|
| 15114 | - @Test
|
|---|
| 15115 | - void testWayDeduplicationSimple() {
|
|---|
| 15116 | - final VectorDataSet dataSet = this.layer.getData();
|
|---|
| 15117 | - assertTrue(dataSet.allPrimitives().isEmpty());
|
|---|
| 15118 | -
|
|---|
| 15119 | - // Set the zoom to 14, as that is the tile we are checking
|
|---|
| 15120 | - dataSet.setZoom(14);
|
|---|
| 15121 | - // Load tiles that are next to each other
|
|---|
| 15122 | - loadTile(this.layer, 14, 3248, 6258, 14, 3249, 6258);
|
|---|
| 15123 | -
|
|---|
| 15124 | - Map<Long, List<VectorWay>> wayGroups = dataSet.getWays().stream()
|
|---|
| 15125 | - .collect(Collectors.groupingBy(VectorWay::getId));
|
|---|
| 15126 | - wayGroups.forEach((id, ways) -> assertEquals(1, ways.size(), MessageFormat.format("{0} was not deduplicated", id)));
|
|---|
| 15127 | - }
|
|---|
| 15128 | }
|
|---|
| 15129 | --
|
|---|
| 15130 | GitLab
|
|---|
| 15131 |
|
|---|
| 15132 |
|
|---|
| 15133 | From 48a0c94f8a31ca8e57e3b3dce677fc034d1d8303 Mon Sep 17 00:00:00 2001
|
|---|
| 15134 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15135 | Date: Tue, 4 May 2021 14:51:45 -0600
|
|---|
| 15136 | Subject: [PATCH 43/50] VectorDataSet: Enable custom data for layers
|
|---|
| 15137 |
|
|---|
| 15138 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15139 | ---
|
|---|
| 15140 | .../josm/data/vector/VectorDataSet.java | 50 ++++++++-----------
|
|---|
| 15141 | 1 file changed, 21 insertions(+), 29 deletions(-)
|
|---|
| 15142 |
|
|---|
| 15143 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15144 | index d626156d4..afb958232 100644
|
|---|
| 15145 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15146 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15147 | @@ -168,11 +168,9 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15148 | public List<VectorNode> searchNodes(BBox bbox) {
|
|---|
| 15149 | return tryRead(this.readWriteLock, () -> {
|
|---|
| 15150 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15151 | - if (dataStore != null) {
|
|---|
| 15152 | - return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 15153 | - .flatMap(store -> store.searchNodes(bbox).stream()).collect(Collectors.toList());
|
|---|
| 15154 | - }
|
|---|
| 15155 | - return null;
|
|---|
| 15156 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15157 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getStore)
|
|---|
| 15158 | + .flatMap(store -> store.searchNodes(bbox).stream()).collect(Collectors.toList());
|
|---|
| 15159 | }).orElseGet(Collections::emptyList);
|
|---|
| 15160 | }
|
|---|
| 15161 |
|
|---|
| 15162 | @@ -180,9 +178,9 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15163 | public boolean containsNode(VectorNode vectorNode) {
|
|---|
| 15164 | return tryRead(this.readWriteLock, () -> {
|
|---|
| 15165 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15166 | - return dataStore != null &&
|
|---|
| 15167 | - dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 15168 | - .anyMatch(store -> store.containsNode(vectorNode));
|
|---|
| 15169 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15170 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getStore)
|
|---|
| 15171 | + .anyMatch(store -> store.containsNode(vectorNode));
|
|---|
| 15172 | }).orElse(Boolean.FALSE);
|
|---|
| 15173 | }
|
|---|
| 15174 |
|
|---|
| 15175 | @@ -190,11 +188,9 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15176 | public List<VectorWay> searchWays(BBox bbox) {
|
|---|
| 15177 | return tryRead(this.readWriteLock, () -> {
|
|---|
| 15178 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15179 | - if (dataStore != null) {
|
|---|
| 15180 | - return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 15181 | - .flatMap(store -> store.searchWays(bbox).stream()).collect(Collectors.toList());
|
|---|
| 15182 | - }
|
|---|
| 15183 | - return null;
|
|---|
| 15184 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15185 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getStore)
|
|---|
| 15186 | + .flatMap(store -> store.searchWays(bbox).stream()).collect(Collectors.toList());
|
|---|
| 15187 | }).orElseGet(Collections::emptyList);
|
|---|
| 15188 | }
|
|---|
| 15189 |
|
|---|
| 15190 | @@ -202,9 +198,9 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15191 | public boolean containsWay(VectorWay vectorWay) {
|
|---|
| 15192 | return tryRead(this.readWriteLock, () -> {
|
|---|
| 15193 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15194 | - return dataStore != null &&
|
|---|
| 15195 | - dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 15196 | - .anyMatch(store -> store.containsWay(vectorWay));
|
|---|
| 15197 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15198 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getStore)
|
|---|
| 15199 | + .anyMatch(store -> store.containsWay(vectorWay));
|
|---|
| 15200 | }).orElse(Boolean.FALSE);
|
|---|
| 15201 | }
|
|---|
| 15202 |
|
|---|
| 15203 | @@ -212,11 +208,9 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15204 | public List<VectorRelation> searchRelations(BBox bbox) {
|
|---|
| 15205 | return tryRead(this.readWriteLock, () -> {
|
|---|
| 15206 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15207 | - if (dataStore != null) {
|
|---|
| 15208 | - return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 15209 | - .flatMap(store -> store.searchRelations(bbox).stream()).collect(Collectors.toList());
|
|---|
| 15210 | - }
|
|---|
| 15211 | - return null;
|
|---|
| 15212 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15213 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getStore)
|
|---|
| 15214 | + .flatMap(store -> store.searchRelations(bbox).stream()).collect(Collectors.toList());
|
|---|
| 15215 | }).orElseGet(Collections::emptyList);
|
|---|
| 15216 | }
|
|---|
| 15217 |
|
|---|
| 15218 | @@ -224,9 +218,9 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15219 | public boolean containsRelation(VectorRelation vectorRelation) {
|
|---|
| 15220 | return tryRead(this.readWriteLock, () -> {
|
|---|
| 15221 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15222 | - return dataStore != null &&
|
|---|
| 15223 | - dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getStore)
|
|---|
| 15224 | - .anyMatch(store -> store.containsRelation(vectorRelation));
|
|---|
| 15225 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15226 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getStore)
|
|---|
| 15227 | + .anyMatch(store -> store.containsRelation(vectorRelation));
|
|---|
| 15228 | }).orElse(Boolean.FALSE);
|
|---|
| 15229 | }
|
|---|
| 15230 |
|
|---|
| 15231 | @@ -248,11 +242,9 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15232 | */
|
|---|
| 15233 | public Stream<VectorPrimitive> getPrimitivesById(PrimitiveId... primitiveIds) {
|
|---|
| 15234 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15235 | - if (dataStore != null) {
|
|---|
| 15236 | - return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getPrimitivesMap)
|
|---|
| 15237 | - .flatMap(m -> Stream.of(primitiveIds).map(m::get)).filter(Objects::nonNull);
|
|---|
| 15238 | - }
|
|---|
| 15239 | - return Stream.empty();
|
|---|
| 15240 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15241 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getPrimitivesMap)
|
|---|
| 15242 | + .flatMap(m -> Stream.of(primitiveIds).map(m::get)).filter(Objects::nonNull);
|
|---|
| 15243 | }
|
|---|
| 15244 |
|
|---|
| 15245 | @Override
|
|---|
| 15246 | --
|
|---|
| 15247 | GitLab
|
|---|
| 15248 |
|
|---|
| 15249 |
|
|---|
| 15250 | From 556c1c75df8fdef7fbf1e749a4b4d1e3a62b5aac Mon Sep 17 00:00:00 2001
|
|---|
| 15251 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15252 | Date: Tue, 4 May 2021 16:04:46 -0600
|
|---|
| 15253 | Subject: [PATCH 44/50] VectorDataSet: Get all primitives with the id
|
|---|
| 15254 |
|
|---|
| 15255 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15256 | ---
|
|---|
| 15257 | src/org/openstreetmap/josm/data/vector/VectorDataSet.java | 8 ++++----
|
|---|
| 15258 | 1 file changed, 4 insertions(+), 4 deletions(-)
|
|---|
| 15259 |
|
|---|
| 15260 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15261 | index afb958232..0e8b02ea5 100644
|
|---|
| 15262 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15263 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15264 | @@ -429,7 +429,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15265 |
|
|---|
| 15266 | private void toggleSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 15267 | this.doSelectionChange(old -> new IDataSelectionListener.SelectionToggleEvent<>(this, old,
|
|---|
| 15268 | - osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 15269 | + osm.flatMap(this::getPrimitivesById).filter(Objects::nonNull)));
|
|---|
| 15270 | }
|
|---|
| 15271 |
|
|---|
| 15272 | @Override
|
|---|
| 15273 | @@ -444,7 +444,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15274 |
|
|---|
| 15275 | private void setSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 15276 | this.doSelectionChange(old -> new IDataSelectionListener.SelectionReplaceEvent<>(this, old,
|
|---|
| 15277 | - osm.filter(Objects::nonNull).map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 15278 | + osm.filter(Objects::nonNull).flatMap(this::getPrimitivesById).filter(Objects::nonNull)));
|
|---|
| 15279 | }
|
|---|
| 15280 |
|
|---|
| 15281 | @Override
|
|---|
| 15282 | @@ -459,7 +459,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15283 |
|
|---|
| 15284 | private void addSelectedImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 15285 | this.doSelectionChange(old -> new IDataSelectionListener.SelectionAddEvent<>(this, old,
|
|---|
| 15286 | - osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 15287 | + osm.flatMap(this::getPrimitivesById).filter(Objects::nonNull)));
|
|---|
| 15288 | }
|
|---|
| 15289 |
|
|---|
| 15290 | @Override
|
|---|
| 15291 | @@ -479,7 +479,7 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15292 |
|
|---|
| 15293 | private void clearSelectionImpl(Stream<? extends PrimitiveId> osm) {
|
|---|
| 15294 | this.doSelectionChange(old -> new IDataSelectionListener.SelectionRemoveEvent<>(this, old,
|
|---|
| 15295 | - osm.map(this::getPrimitiveById).filter(Objects::nonNull)));
|
|---|
| 15296 | + osm.flatMap(this::getPrimitivesById).filter(Objects::nonNull)));
|
|---|
| 15297 | }
|
|---|
| 15298 |
|
|---|
| 15299 | /**
|
|---|
| 15300 | --
|
|---|
| 15301 | GitLab
|
|---|
| 15302 |
|
|---|
| 15303 |
|
|---|
| 15304 | From 710dd4df7644917bc8072f674265e36ccac3b56a Mon Sep 17 00:00:00 2001
|
|---|
| 15305 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15306 | Date: Wed, 5 May 2021 09:08:59 -0600
|
|---|
| 15307 | Subject: [PATCH 45/50] LayersTest: Fix width/radius issue
|
|---|
| 15308 |
|
|---|
| 15309 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15310 | ---
|
|---|
| 15311 | .../josm/data/imagery/vectortile/mapbox/style/LayersTest.java | 2 +-
|
|---|
| 15312 | 1 file changed, 1 insertion(+), 1 deletion(-)
|
|---|
| 15313 |
|
|---|
| 15314 | diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 15315 | index 5db4d5a3d..d4b87daa0 100644
|
|---|
| 15316 | --- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 15317 | +++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/style/LayersTest.java
|
|---|
| 15318 | @@ -285,7 +285,7 @@ class LayersTest {
|
|---|
| 15319 | .build());
|
|---|
| 15320 | assertEquals("node::random-layer-id{icon-image:concat(\"something/\",tag(\"value\"),\"/random\");icon-offset-x:2.0;"
|
|---|
| 15321 | + "icon-offset-y:3.0;icon-opacity:0.5;icon-rotation:30.0;text-color:#fffff0;text:something;font-family:\"SansSerif\";"
|
|---|
| 15322 | - + "font-weight:normal;font-style:normal;text-halo-color:#ffffff;text-halo-radius:16;text-opacity:0.6;font-size:25;}",
|
|---|
| 15323 | + + "font-weight:normal;font-style:normal;text-halo-color:#ffffff;text-halo-radius:8;text-opacity:0.6;font-size:25;}",
|
|---|
| 15324 | fullOneIconImagePlaceholderExtraLineLayer.toString());
|
|---|
| 15325 |
|
|---|
| 15326 | // Test with placeholders in icon-image
|
|---|
| 15327 | --
|
|---|
| 15328 | GitLab
|
|---|
| 15329 |
|
|---|
| 15330 |
|
|---|
| 15331 | From d836e105168cfb32444ff44296bffe17ab1a1436 Mon Sep 17 00:00:00 2001
|
|---|
| 15332 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15333 | Date: Wed, 5 May 2021 09:19:11 -0600
|
|---|
| 15334 | Subject: [PATCH 46/50] VectorDataSet: Use custom data layer when getting all
|
|---|
| 15335 | primitives
|
|---|
| 15336 |
|
|---|
| 15337 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15338 | ---
|
|---|
| 15339 | src/org/openstreetmap/josm/data/vector/VectorDataSet.java | 6 ++----
|
|---|
| 15340 | 1 file changed, 2 insertions(+), 4 deletions(-)
|
|---|
| 15341 |
|
|---|
| 15342 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15343 | index 0e8b02ea5..1851603a4 100644
|
|---|
| 15344 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15345 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15346 | @@ -251,10 +251,8 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15347 | public <T extends VectorPrimitive> Collection<T> getPrimitives(Predicate<? super VectorPrimitive> predicate) {
|
|---|
| 15348 | Collection<VectorPrimitive> primitives = tryRead(this.readWriteLock, () -> {
|
|---|
| 15349 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15350 | - if (dataStore == null) {
|
|---|
| 15351 | - return null;
|
|---|
| 15352 | - }
|
|---|
| 15353 | - return dataStore.stream().map(MVTTile::getData)
|
|---|
| 15354 | + final Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15355 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore))
|
|---|
| 15356 | .map(VectorDataStore::getAllPrimitives).flatMap(Collection::stream).distinct().collect(Collectors.toList());
|
|---|
| 15357 |
|
|---|
| 15358 | }).orElseGet(Collections::emptyList);
|
|---|
| 15359 | --
|
|---|
| 15360 | GitLab
|
|---|
| 15361 |
|
|---|
| 15362 |
|
|---|
| 15363 | From c3f785230a09868681023c4ff953b69ea25cf2c8 Mon Sep 17 00:00:00 2001
|
|---|
| 15364 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15365 | Date: Wed, 5 May 2021 16:38:17 -0600
|
|---|
| 15366 | Subject: [PATCH 47/50] VectorDataSet: Get selection from custom data as well
|
|---|
| 15367 |
|
|---|
| 15368 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15369 | ---
|
|---|
| 15370 | .../josm/data/vector/VectorDataSet.java | 20 ++++++++-----------
|
|---|
| 15371 | 1 file changed, 8 insertions(+), 12 deletions(-)
|
|---|
| 15372 |
|
|---|
| 15373 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15374 | index 1851603a4..a87fd34fa 100644
|
|---|
| 15375 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15376 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataSet.java
|
|---|
| 15377 | @@ -360,18 +360,14 @@ public class VectorDataSet implements OsmData<VectorPrimitive, VectorNode, Vecto
|
|---|
| 15378 | public Collection<VectorPrimitive> getAllSelected() {
|
|---|
| 15379 | return tryRead(this.readWriteLock, () -> {
|
|---|
| 15380 | final Storage<MVTTile> dataStore = this.getBestZoomDataStore().orElse(null);
|
|---|
| 15381 | - if (dataStore != null) {
|
|---|
| 15382 | - // The dataStore is what we don't want to concurrently modify
|
|---|
| 15383 | - synchronized (dataStore) {
|
|---|
| 15384 | - return dataStore.stream().map(MVTTile::getData).map(VectorDataStore::getPrimitivesMap).flatMap(dataMap -> {
|
|---|
| 15385 | - // Synchronize on dataMap to avoid concurrent modification errors
|
|---|
| 15386 | - synchronized (dataMap) {
|
|---|
| 15387 | - return this.currentSelectedPrimitives.stream().map(dataMap::get).filter(Objects::nonNull);
|
|---|
| 15388 | - }
|
|---|
| 15389 | - }).collect(Collectors.toList());
|
|---|
| 15390 | - }
|
|---|
| 15391 | - }
|
|---|
| 15392 | - return null;
|
|---|
| 15393 | + Stream<VectorDataStore> dataStoreStream = dataStore != null ? dataStore.stream().map(MVTTile::getData) : Stream.empty();
|
|---|
| 15394 | + return Stream.concat(dataStoreStream, Stream.of(this.customDataStore)).map(VectorDataStore::getPrimitivesMap)
|
|---|
| 15395 | + .flatMap(dataMap -> {
|
|---|
| 15396 | + // Synchronize on dataMap to avoid concurrent modification errors
|
|---|
| 15397 | + synchronized (dataMap) {
|
|---|
| 15398 | + return this.currentSelectedPrimitives.stream().map(dataMap::get).filter(Objects::nonNull);
|
|---|
| 15399 | + }
|
|---|
| 15400 | + }).collect(Collectors.toList());
|
|---|
| 15401 | }).orElseGet(Collections::emptyList);
|
|---|
| 15402 | }
|
|---|
| 15403 |
|
|---|
| 15404 | --
|
|---|
| 15405 | GitLab
|
|---|
| 15406 |
|
|---|
| 15407 |
|
|---|
| 15408 | From d4ea49dea0b95392a86e66798b65fbfd206197fc Mon Sep 17 00:00:00 2001
|
|---|
| 15409 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15410 | Date: Wed, 5 May 2021 16:38:33 -0600
|
|---|
| 15411 | Subject: [PATCH 48/50] VectorDataSetTest: PMD/checkstyle
|
|---|
| 15412 |
|
|---|
| 15413 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15414 | ---
|
|---|
| 15415 | src/org/openstreetmap/josm/data/vector/VectorDataStore.java | 4 +++-
|
|---|
| 15416 | .../org/openstreetmap/josm/data/vector/VectorDataSetTest.java | 3 ++-
|
|---|
| 15417 | 2 files changed, 5 insertions(+), 2 deletions(-)
|
|---|
| 15418 |
|
|---|
| 15419 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 15420 | index dceef3b8e..2d1cd17d5 100644
|
|---|
| 15421 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 15422 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 15423 | @@ -40,6 +40,8 @@ import java.util.stream.Collectors;
|
|---|
| 15424 | public class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, VectorWay, VectorRelation> implements Destroyable {
|
|---|
| 15425 | private static final String JOSM_MERGE_TYPE_KEY = "josm_merge_type";
|
|---|
| 15426 | private static final String ORIGINAL_ID = "original_id";
|
|---|
| 15427 | + private static final String MULTIPOLYGON_TYPE = "multipolygon";
|
|---|
| 15428 | + private static final String RELATION_TYPE = "type";
|
|---|
| 15429 |
|
|---|
| 15430 | @Override
|
|---|
| 15431 | protected void addPrimitive(VectorPrimitive primitive) {
|
|---|
| 15432 | @@ -292,7 +294,7 @@ public class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, Vect
|
|---|
| 15433 | .orElse(null);
|
|---|
| 15434 | } else if (shape instanceof Area) {
|
|---|
| 15435 | primitive = areaToRelation(tile, layer, featureObjects, (Area) shape);
|
|---|
| 15436 | - primitive.put("type", "multipolygon");
|
|---|
| 15437 | + primitive.put(RELATION_TYPE, MULTIPOLYGON_TYPE);
|
|---|
| 15438 | } else {
|
|---|
| 15439 | // We shouldn't hit this, but just in case
|
|---|
| 15440 | throw new UnsupportedOperationException();
|
|---|
| 15441 | diff --git a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 15442 | index c0a1c8532..69035abc0 100644
|
|---|
| 15443 | --- a/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 15444 | +++ b/test/unit/org/openstreetmap/josm/data/vector/VectorDataSetTest.java
|
|---|
| 15445 | @@ -118,7 +118,8 @@ class VectorDataSetTest {
|
|---|
| 15446 | // (MVT v2 explicitly said that ids had to be unique in a layer, MVT v1 did not)
|
|---|
| 15447 | // This number is from the 56 nodes - original node with id - single node on mapillary-sequences layer = 54
|
|---|
| 15448 | assertEquals(54, dataSet.getNodes().stream().filter(node -> node.hasKey("original_id")).count());
|
|---|
| 15449 | - assertEquals(1, dataSet.getNodes().stream().filter(node -> node.hasKey("original_id")).map(node -> node.get("original_id")).distinct().count());
|
|---|
| 15450 | + assertEquals(1, dataSet.getNodes().stream().filter(node -> node.hasKey("original_id")).map(node -> node.get("original_id"))
|
|---|
| 15451 | + .distinct().count());
|
|---|
| 15452 | assertEquals(1, dataSet.getWays().size());
|
|---|
| 15453 | assertEquals(0, dataSet.getRelations().size());
|
|---|
| 15454 | }
|
|---|
| 15455 | --
|
|---|
| 15456 | GitLab
|
|---|
| 15457 |
|
|---|
| 15458 |
|
|---|
| 15459 | From a1387d79c5fa547b0b4ba401cf54b7cc2659f55b Mon Sep 17 00:00:00 2001
|
|---|
| 15460 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15461 | Date: Thu, 6 May 2021 07:14:06 -0600
|
|---|
| 15462 | Subject: [PATCH 49/50] Mapbox Vector Tiles: Modify file formats and their
|
|---|
| 15463 | documentation
|
|---|
| 15464 |
|
|---|
| 15465 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15466 | ---
|
|---|
| 15467 | .../josm/data/imagery/vectortile/mapbox/MVTFile.java | 9 +++++----
|
|---|
| 15468 | 1 file changed, 5 insertions(+), 4 deletions(-)
|
|---|
| 15469 |
|
|---|
| 15470 | diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java
|
|---|
| 15471 | index 84ac8ae89..7a398537b 100644
|
|---|
| 15472 | --- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java
|
|---|
| 15473 | +++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MVTFile.java
|
|---|
| 15474 | @@ -13,15 +13,16 @@ import java.util.List;
|
|---|
| 15475 | public final class MVTFile {
|
|---|
| 15476 | /**
|
|---|
| 15477 | * Extensions for Mapbox Vector Tiles.
|
|---|
| 15478 | - * This is a SHOULD, <i>not</i> a MUST.
|
|---|
| 15479 | + * {@code mvt} is a SHOULD, <i>not</i> a MUST.
|
|---|
| 15480 | */
|
|---|
| 15481 | - public static final List<String> EXTENSION = Collections.unmodifiableList(Arrays.asList("mvt"));
|
|---|
| 15482 | + public static final List<String> EXTENSION = Collections.unmodifiableList(Arrays.asList("mvt", "pbf"));
|
|---|
| 15483 |
|
|---|
| 15484 | /**
|
|---|
| 15485 | * mimetypes for Mapbox Vector Tiles
|
|---|
| 15486 | - * This is a SHOULD, <i>not</i> a MUST.
|
|---|
| 15487 | + * This {@code application/vnd.mapbox-vector-tile}is a SHOULD, <i>not</i> a MUST.
|
|---|
| 15488 | */
|
|---|
| 15489 | - public static final List<String> MIMETYPE = Collections.unmodifiableList(Arrays.asList("application/vnd.mapbox-vector-tile"));
|
|---|
| 15490 | + public static final List<String> MIMETYPE = Collections.unmodifiableList(Arrays.asList("application/vnd.mapbox-vector-tile",
|
|---|
| 15491 | + "application/x-protobuf"));
|
|---|
| 15492 |
|
|---|
| 15493 | /**
|
|---|
| 15494 | * The default projection. This is Web Mercator, per specification.
|
|---|
| 15495 | --
|
|---|
| 15496 | GitLab
|
|---|
| 15497 |
|
|---|
| 15498 |
|
|---|
| 15499 | From 3ee61eadda7b58999c17d5f8bd3706cada90c8ba Mon Sep 17 00:00:00 2001
|
|---|
| 15500 | From: Taylor Smock <tsmock@fb.com>
|
|---|
| 15501 | Date: Thu, 6 May 2021 07:19:10 -0600
|
|---|
| 15502 | Subject: [PATCH 50/50] Vector Tiles: Account for features with no tags
|
|---|
| 15503 |
|
|---|
| 15504 | Signed-off-by: Taylor Smock <tsmock@fb.com>
|
|---|
| 15505 | ---
|
|---|
| 15506 | src/org/openstreetmap/josm/data/vector/VectorDataStore.java | 4 +++-
|
|---|
| 15507 | 1 file changed, 3 insertions(+), 1 deletion(-)
|
|---|
| 15508 |
|
|---|
| 15509 | diff --git a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 15510 | index 2d1cd17d5..793bbf34a 100644
|
|---|
| 15511 | --- a/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 15512 | +++ b/src/org/openstreetmap/josm/data/vector/VectorDataStore.java
|
|---|
| 15513 | @@ -323,7 +323,9 @@ public class VectorDataStore extends DataStore<VectorPrimitive, VectorNode, Vect
|
|---|
| 15514 | primitive.put(ORIGINAL_ID, Long.toString(feature.getId()));
|
|---|
| 15515 | primitive.setId(primitive.getIdGenerator().generateUniqueId());
|
|---|
| 15516 | }
|
|---|
| 15517 | - feature.getTags().forEach(primitive::put);
|
|---|
| 15518 | + if (feature.getTags() != null) {
|
|---|
| 15519 | + feature.getTags().forEach(primitive::put);
|
|---|
| 15520 | + }
|
|---|
| 15521 | featureObjects.forEach(this::addPrimitive);
|
|---|
| 15522 | primaryFeatureObjects.forEach(this::addPrimitive);
|
|---|
| 15523 | try {
|
|---|
| 15524 | --
|
|---|
| 15525 | GitLab
|
|---|
| 15526 |
|
|---|