1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.locationtech.spatial4j.io;
20
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.io.StringReader;
24 import java.text.ParseException;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.locationtech.spatial4j.context.SpatialContext;
29 import org.locationtech.spatial4j.context.SpatialContextFactory;
30 import org.locationtech.spatial4j.exception.InvalidShapeException;
31 import org.locationtech.spatial4j.shape.Shape;
32 import org.locationtech.spatial4j.shape.ShapeFactory;
33 import org.locationtech.jts.geom.LinearRing;
34
35
36
37
38
39 public class PolyshapeReader implements ShapeReader {
40 final SpatialContext ctx;
41 final ShapeFactory shpFactory;
42
43 public PolyshapeReader(SpatialContext ctx, SpatialContextFactory factory) {
44 this.ctx = ctx;
45 this.shpFactory = ctx.getShapeFactory();
46 }
47
48 @Override
49 public String getFormatName() {
50 return ShapeIO.POLY;
51 }
52
53 @Override
54 public Shape read(Object value) throws IOException, ParseException, InvalidShapeException {
55 return read(new StringReader(value.toString().trim()));
56 }
57
58 @Override
59 public Shape readIfSupported(Object value) throws InvalidShapeException {
60 String v = value.toString().trim();
61 char first = v.charAt(0);
62 if(first >= '0' && first <= '9') {
63 try {
64 return read(new StringReader(v));
65 } catch (ParseException e) {
66 } catch (IOException e) {
67 }
68 }
69 return null;
70 }
71
72
73
74
75
76 @Override
77 public final Shape read(Reader r) throws ParseException, IOException
78 {
79 XReader reader = new XReader(r, shpFactory);
80 Double arg = null;
81
82 Shape lastShape = null;
83 List<Shape> shapes = null;
84 while(!reader.isDone()) {
85 char event = reader.readKey();
86 if(event<'0' || event > '9') {
87 if(event == PolyshapeWriter.KEY_SEPERATOR) {
88 continue;
89 }
90 throw new ParseException("expecting a shape key. not '"+event+"'", -1);
91 }
92
93 if(lastShape!=null) {
94 if(shapes==null) {
95 shapes = new ArrayList<>();
96 }
97 shapes.add(lastShape);
98 }
99 arg = null;
100
101 if(reader.peek()==PolyshapeWriter.KEY_ARG_START) {
102 reader.readKey();
103 arg = reader.readDouble();
104 if(reader.readKey()!=PolyshapeWriter.KEY_ARG_END) {
105 throw new ParseException("expecting an argument end", -1);
106 }
107 }
108 if(reader.isEvent()) {
109 throw new ParseException("Invalid input. Event should be followed by data", -1);
110 }
111
112 switch(event) {
113 case PolyshapeWriter.KEY_POINT: {
114 lastShape = shpFactory.pointXY(shpFactory.normX(reader.readLat()), shpFactory.normY(reader.readLng()));
115 break;
116 }
117 case PolyshapeWriter.KEY_LINE: {
118 ShapeFactory.LineStringBuilder lineBuilder = shpFactory.lineString();
119 reader.readPoints(lineBuilder);
120
121 if(arg!=null) {
122 lineBuilder.buffer(shpFactory.normDist(arg));
123 }
124 lastShape = lineBuilder.build();
125 break;
126 }
127 case PolyshapeWriter.KEY_BOX: {
128 double lat1 = shpFactory.normX(reader.readLat());
129 double lon1 = shpFactory.normY(reader.readLng());
130 lastShape = shpFactory.rect(lat1, shpFactory.normX(reader.readLat()),
131 lon1, shpFactory.normY(reader.readLng()));
132 break;
133 }
134 case PolyshapeWriter.KEY_MULTIPOINT : {
135 lastShape = reader.readPoints(shpFactory.multiPoint()).build();
136 break;
137 }
138 case PolyshapeWriter.KEY_CIRCLE : {
139 if(arg==null) {
140 throw new IllegalArgumentException("the input should have a radius argument");
141 }
142 lastShape = shpFactory.circle(shpFactory.normX(reader.readLat()), shpFactory.normY(reader.readLng()),
143 shpFactory.normDist(arg.doubleValue()));
144 break;
145 }
146 case PolyshapeWriter.KEY_POLYGON: {
147 lastShape = readPolygon(reader);
148 break;
149 }
150 default: {
151 throw new ParseException("unhandled key: "+event, -1);
152 }
153 }
154 }
155
156 if(shapes!=null) {
157 if(lastShape!=null) {
158 shapes.add(lastShape);
159 }
160
161 ShapeFactory.MultiShapeBuilder<Shape> multiBuilder = shpFactory.multiShape(Shape.class);
162 for (Shape shp : shapes) {
163 multiBuilder.add(shp);
164 }
165
166 return multiBuilder.build();
167 }
168 return lastShape;
169 }
170
171 protected Shape readPolygon(XReader reader) throws IOException {
172 ShapeFactory.PolygonBuilder polyBuilder = shpFactory.polygon();
173
174 reader.readPoints(polyBuilder);
175
176 if(!reader.isDone() && reader.peek()==PolyshapeWriter.KEY_ARG_START) {
177 while(reader.isEvent() && reader.peek()==PolyshapeWriter.KEY_ARG_START) {
178 reader.readKey();
179 reader.readPoints(polyBuilder.hole()).endHole();
180 }
181 }
182
183 return polyBuilder.build();
184 }
185
186
187
188
189
190 public static class XReader {
191 int lat = 0;
192 int lng = 0;
193
194 int head = -1;
195 final Reader input;
196 final ShapeFactory shpFactory;
197
198 public XReader(final Reader input, ShapeFactory shpFactory) throws IOException {
199 this.input = input;
200 this.shpFactory = shpFactory;
201 head = input.read();
202 }
203
204 public <T extends ShapeFactory.PointsBuilder> T readPoints(T builder) throws IOException {
205 while(isData()) {
206 builder.pointXY(shpFactory.normX(readLat()), shpFactory.normY(readLng()));
207 }
208 return builder;
209 }
210
211 public double readLat() throws IOException {
212 lat += readInt();
213 return lat * 1e-5;
214 }
215
216 public double readLng() throws IOException {
217 lng += readInt();
218 return lng * 1e-5;
219 }
220
221 public double readDouble() throws IOException {
222 return readInt() * 1e-5;
223 }
224
225 public int peek() {
226 return head;
227 }
228
229 public char readKey() throws IOException {
230 lat = lng = 0;
231 char key = (char)head;
232 head = input.read();
233 return key;
234 }
235
236 public boolean isData() {
237 return head >= '?';
238 }
239
240 public boolean isDone() {
241 return head < 0;
242 }
243
244 public boolean isEvent() {
245 return head > 0 && head < '?';
246 }
247
248 int readInt() throws IOException
249 {
250 int b;
251 int result = 1;
252 int shift = 0;
253 do {
254 b = head - 63 - 1;
255 result += b << shift;
256 shift += 5;
257
258 head = input.read();
259 } while (b >= 0x1f);
260 return (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
261 }
262 }
263 }