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.jts;
10  
11  import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
12  import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
13  import org.locationtech.spatial4j.exception.InvalidShapeException;
14  import org.locationtech.spatial4j.io.BinaryCodec;
15  import org.locationtech.spatial4j.shape.Shape;
16  import org.locationtech.jts.geom.Geometry;
17  import org.locationtech.jts.geom.PrecisionModel;
18  import org.locationtech.jts.io.InStream;
19  import org.locationtech.jts.io.OutStream;
20  import org.locationtech.jts.io.ParseException;
21  import org.locationtech.jts.io.WKBConstants;
22  import org.locationtech.jts.io.WKBReader;
23  import org.locationtech.jts.io.WKBWriter;
24  
25  import java.io.DataInput;
26  import java.io.DataOutput;
27  import java.io.IOException;
28  
29  /**
30   * Writes shapes in WKB, if it isn't otherwise supported by the superclass.
31   */
32  public class JtsBinaryCodec extends BinaryCodec {
33  
34    protected final boolean useFloat;//instead of double
35  
36    public JtsBinaryCodec(JtsSpatialContext ctx, JtsSpatialContextFactory factory) {
37      super(ctx, factory);
38      //note: ctx.geometryFactory hasn't been set yet
39      useFloat = (factory.precisionModel.getType() == PrecisionModel.FLOATING_SINGLE);
40    }
41  
42    @Override
43    protected double readDim(DataInput dataInput) throws IOException {
44      if (useFloat)
45        return dataInput.readFloat();
46      return super.readDim(dataInput);
47    }
48  
49    @Override
50    protected void writeDim(DataOutput dataOutput, double v) throws IOException {
51      if (useFloat)
52        dataOutput.writeFloat((float) v);
53      else
54        super.writeDim(dataOutput, v);
55    }
56  
57    @Override
58    protected byte typeForShape(Shape s) {
59      byte type = super.typeForShape(s);
60      if (type == 0) {
61        type = TYPE_GEOM;//handles everything
62      }
63      return type;
64    }
65  
66    @Override
67    protected Shape readShapeByTypeIfSupported(final DataInput dataInput, byte type) throws IOException {
68      if (type != TYPE_GEOM)
69        return super.readShapeByTypeIfSupported(dataInput, type);
70      return readJtsGeom(dataInput);
71    }
72  
73    @Override
74    protected boolean writeShapeByTypeIfSupported(DataOutput dataOutput, Shape s, byte type) throws IOException {
75      if (type != TYPE_GEOM)
76        return super.writeShapeByTypeIfSupported(dataOutput, s, type);
77      writeJtsGeom(dataOutput, s);
78      return true;
79    }
80  
81    public Shape readJtsGeom(final DataInput dataInput) throws IOException {
82      JtsSpatialContext ctx = (JtsSpatialContext)super.ctx;
83      WKBReader reader = new WKBReader(ctx.getGeometryFactory());
84      try {
85        InStream inStream = new InStream() {//a strange JTS abstraction
86          boolean first = true;
87          @Override
88          public void read(byte[] buf) throws IOException {
89            if (first) {//we don't write JTS's leading BOM so synthesize reading it
90              if (buf.length != 1)
91                throw new IllegalStateException("Expected initial read of one byte, not: " + buf.length);
92              buf[0] = WKBConstants.wkbXDR;//0
93              first = false;
94            } else {
95              //TODO for performance, specialize for common array lengths: 1, 4, 8
96              dataInput.readFully(buf);
97            }
98          }
99        };
100       Geometry geom = reader.read(inStream);
101       //false: don't check for dateline-180 cross or multi-polygon overlaps; this won't happen
102       // once it gets written, and we're reading it now
103       return ctx.makeShape(geom, false, false);
104     } catch (ParseException ex) {
105       throw new InvalidShapeException("error reading WKT", ex);
106     }
107   }
108 
109   public void writeJtsGeom(final DataOutput dataOutput, Shape s) throws IOException {
110     JtsSpatialContext ctx = (JtsSpatialContext)super.ctx;
111     Geometry geom = ctx.getGeometryFrom(s);//might even translate it
112     new WKBWriter().write(geom, new OutStream() {//a strange JTS abstraction
113       boolean first = true;
114       @Override
115       public void write(byte[] buf, int len) throws IOException {
116         if (first) {
117           first = false;
118           //skip byte order mark
119           if (len != 1 || buf[0] != WKBConstants.wkbXDR)//the default
120             throw new IllegalStateException("Unexpected WKB byte order mark");
121           return;
122         }
123         dataOutput.write(buf, 0, len);
124       }
125     });
126   }
127 }