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

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

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

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

File size: 4.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Shape;
7import java.awt.geom.Area;
8import java.awt.geom.Ellipse2D;
9import java.awt.geom.Path2D;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.List;
14
15/**
16 * A class to generate geometry for a vector tile
17 * @author Taylor Smock
18 * @since xxx
19 */
20public class Geometry {
21 final Collection<Shape> shapes = new ArrayList<>();
22
23 /**
24 * Create a {@link Geometry} for a {@link Feature}
25 * @param geometryType The type of geometry
26 * @param commands The commands used to create the geometry
27 */
28 public Geometry(GeometryTypes geometryType, List<CommandInteger> commands) {
29 if (geometryType == GeometryTypes.POINT) {
30 for (CommandInteger command : commands) {
31 final short[] operations = command.getOperations();
32 // Each MoveTo command is a new point
33 if (command.getType() == Command.MoveTo && operations.length % 2 == 0 && operations.length > 0) {
34 for (int i = 0; i < operations.length / 2; i++) {
35 // Just using Ellipse2D since it extends Shape
36 shapes.add(new Ellipse2D.Float(operations[2 * i],
37 operations[2 * i + 1], 0, 0));
38 }
39 } else {
40 throw new IllegalArgumentException(tr("{0} with {1} arguments is not understood", geometryType, operations.length));
41 }
42 }
43 } else if (geometryType == GeometryTypes.LINESTRING || geometryType == GeometryTypes.POLYGON) {
44 Path2D.Float line = null;
45 Area area = null;
46 // MVT uses delta encoding. Each feature starts at (0, 0).
47 double x = 0;
48 double y = 0;
49 // Area is used to determine the inner/outer of a polygon
50 double areaAreaSq = 0;
51 for (CommandInteger command : commands) {
52 final short[] operations = command.getOperations();
53 // Technically, there is no reason why there can be multiple MoveTo operations in one command, but that is undefined behavior
54 if (command.getType() == Command.MoveTo && operations.length == 2) {
55 areaAreaSq = 0;
56 x += operations[0];
57 y += operations[1];
58 line = new Path2D.Float();
59 line.moveTo(x, y);
60 shapes.add(line);
61 } else if (command.getType() == Command.LineTo && operations.length % 2 == 0 && line != null) {
62 for (int i = 0; i < operations.length / 2; i++) {
63 final double lx = x;
64 final double ly = y;
65 x += operations[2 * i];
66 y += operations[2 * i + 1];
67 areaAreaSq += lx * y - x * ly;
68 line.lineTo(x, y);
69 }
70 // ClosePath should only be used with Polygon geometry
71 } else if (geometryType == GeometryTypes.POLYGON && command.getType() == Command.ClosePath && line != null) {
72 shapes.remove(line);
73 // new Area() closes the line if it isn't already closed
74 if (area == null) {
75 area = new Area();
76 shapes.add(area);
77 }
78
79 Area nArea = new Area(line);
80 // SonarLint thinks that this is never > 0. It can be.
81 if (areaAreaSq > 0) {
82 area.add(nArea);
83 } else if (areaAreaSq < 0) {
84 area.exclusiveOr(nArea);
85 } else {
86 throw new IllegalArgumentException(tr("{0} cannot have zero area", geometryType));
87 }
88 } else {
89 throw new IllegalArgumentException(tr("{0} with {1} arguments is not understood", geometryType, operations.length));
90 }
91 }
92 }
93 }
94
95 /**
96 * Get the shapes to draw this geometry with
97 * @return A collection of shapes
98 */
99 public Collection<Shape> getShapes() {
100 return Collections.unmodifiableCollection(this.shapes);
101 }
102}
Note: See TracBrowser for help on using the repository browser.