View Javadoc
1   /*******************************************************************************
2    * Copyright (c) 2015 MITRE
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Apache License, Version 2.0 which
5    * accompanies this distribution and is available at
6    *    http://www.apache.org/licenses/LICENSE-2.0.txt
7    ******************************************************************************/
8   
9   package org.locationtech.spatial4j.io;
10  
11  import org.locationtech.spatial4j.context.SpatialContext;
12  import org.locationtech.spatial4j.context.SpatialContextFactory;
13  import org.locationtech.spatial4j.exception.InvalidShapeException;
14  import org.locationtech.spatial4j.shape.Circle;
15  import org.locationtech.spatial4j.shape.Point;
16  import org.locationtech.spatial4j.shape.Rectangle;
17  import org.locationtech.spatial4j.shape.Shape;
18  import org.locationtech.spatial4j.shape.ShapeCollection;
19  
20  import java.io.DataInput;
21  import java.io.DataOutput;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  
25  /**
26   * A binary shape format. It is <em>not</em> designed to be a published standard, unlike Well Known
27   * Binary (WKB). The initial release is simple but it could get more optimized to use fewer bytes or
28   * to write &amp; read pre-computed index structures.
29   * <p>
30   * Immutable and thread-safe.
31   */
32  public class BinaryCodec {
33    //type 0; reserved for unkonwn/generic; see readCollection
34    protected static final byte
35        TYPE_POINT = 1,
36        TYPE_RECT = 2,
37        TYPE_CIRCLE = 3,
38        TYPE_COLL = 4,
39        TYPE_GEOM = 5;
40  
41    //TODO support BufferedLineString
42  
43    protected final SpatialContext ctx;
44  
45    //This constructor is mandated by SpatialContextFactory
46    public BinaryCodec(SpatialContext ctx, SpatialContextFactory factory) {
47      this.ctx = ctx;
48    }
49  
50    public Shape readShape(DataInput dataInput) throws IOException {
51      byte type = dataInput.readByte();
52      Shape s = readShapeByTypeIfSupported(dataInput, type);
53      if (s == null)
54        throw new IllegalArgumentException("Unsupported shape byte "+type);
55      return s;
56    }
57  
58    public void writeShape(DataOutput dataOutput, Shape s) throws IOException {
59      boolean written = writeShapeByTypeIfSupported(dataOutput, s);
60      if (!written)
61        throw new IllegalArgumentException("Unsupported shape "+s.getClass());
62    }
63  
64    protected Shape readShapeByTypeIfSupported(DataInput dataInput, byte type) throws IOException {
65      switch (type) {
66        case TYPE_POINT: return readPoint(dataInput);
67        case TYPE_RECT: return readRect(dataInput);
68        case TYPE_CIRCLE: return readCircle(dataInput);
69        case TYPE_COLL: return readCollection(dataInput);
70        default: return null;
71      }
72    }
73  
74    /** Note: writes the type byte even if not supported */
75    protected boolean writeShapeByTypeIfSupported(DataOutput dataOutput, Shape s) throws IOException {
76      byte type = typeForShape(s);
77      dataOutput.writeByte(type);
78      return writeShapeByTypeIfSupported(dataOutput, s, type);
79      //dataOutput.position(dataOutput.position() - 1);//reset putting type
80    }
81  
82    protected boolean writeShapeByTypeIfSupported(DataOutput dataOutput, Shape s, byte type) throws IOException {
83      switch (type) {
84        case TYPE_POINT: writePoint(dataOutput, (Point) s); break;
85        case TYPE_RECT: writeRect(dataOutput, (Rectangle) s); break;
86        case TYPE_CIRCLE: writeCircle(dataOutput, (Circle) s); break;
87        case TYPE_COLL: writeCollection(dataOutput, (ShapeCollection) s); break;
88        default:
89          return false;
90      }
91      return true;
92    }
93  
94    protected byte typeForShape(Shape s) {
95      if (s instanceof Point) {
96        return TYPE_POINT;
97      } else if (s instanceof Rectangle) {
98        return TYPE_RECT;
99      } else if (s instanceof Circle) {
100       return TYPE_CIRCLE;
101     } else if (s instanceof ShapeCollection) {
102       return TYPE_COLL;
103     } else {
104       return 0;
105     }
106   }
107 
108   protected double readDim(DataInput dataInput) throws IOException {
109     return dataInput.readDouble();
110   }
111 
112   protected void writeDim(DataOutput dataOutput, double v) throws IOException {
113     dataOutput.writeDouble(v);
114   }
115 
116   public Point readPoint(DataInput dataInput) throws IOException {
117     return ctx.makePoint(readDim(dataInput), readDim(dataInput));
118   }
119 
120   public void writePoint(DataOutput dataOutput, Point pt) throws IOException {
121     writeDim(dataOutput, pt.getX());
122     writeDim(dataOutput, pt.getY());
123   }
124 
125   public Rectangle readRect(DataInput dataInput) throws IOException {
126     return ctx.makeRectangle(readDim(dataInput), readDim(dataInput), readDim(dataInput), readDim(dataInput));
127   }
128 
129   public void writeRect(DataOutput dataOutput, Rectangle r) throws IOException {
130     writeDim(dataOutput, r.getMinX());
131     writeDim(dataOutput, r.getMaxX());
132     writeDim(dataOutput, r.getMinY());
133     writeDim(dataOutput, r.getMaxY());
134   }
135 
136   public Circle readCircle(DataInput dataInput) throws IOException {
137     return ctx.makeCircle(readPoint(dataInput), readDim(dataInput));
138   }
139 
140   public void writeCircle(DataOutput dataOutput, Circle c) throws IOException {
141     writePoint(dataOutput, c.getCenter());
142     writeDim(dataOutput, c.getRadius());
143   }
144 
145   public ShapeCollection readCollection(DataInput dataInput) throws IOException {
146     byte type = dataInput.readByte();
147     int size = dataInput.readInt();
148     ArrayList<Shape> shapes = new ArrayList<>(size);
149     for (int i = 0; i < size; i++) {
150       if (type == 0) {
151         shapes.add(readShape(dataInput));
152       } else {
153         Shape s = readShapeByTypeIfSupported(dataInput, type);
154         if (s == null)
155           throw new InvalidShapeException("Unsupported shape byte "+type);
156         shapes.add(s);
157       }
158     }
159     return ctx.makeCollection(shapes);
160   }
161 
162   public void writeCollection(DataOutput dataOutput, ShapeCollection col) throws IOException {
163     byte type = (byte) 0;//TODO add type to ShapeCollection
164     dataOutput.writeByte(type);
165     dataOutput.writeInt(col.size());
166     for (int i = 0; i < col.size(); i++) {
167       Shape s = col.get(i);
168       if (type == 0) {
169         writeShape(dataOutput, s);
170       } else {
171         boolean written = writeShapeByTypeIfSupported(dataOutput, s, type);
172         if (!written)
173           throw new IllegalArgumentException("Unsupported shape type "+s.getClass());
174       }
175     }
176   }
177 
178 }