Loading MySQL Spatial Data with JDBC and JTS WKBReader

When it comes to open source spatial databases, PostGIS gets the lion’s share of attention, but MySQL has come a long way in its support of spatial features.   Still, if you want to load spatial data from JDBC, the MySQL Connector/J JDBC libraries don’t provide Java classes for converting the data to spatial types, so you need to find another solution.

One approach is  Geotools, which provides a datastore for loading features from MySQL.  However, if you want something more lightweight, it’s fairly straightforward  to load up a feature using the JTS WKBReader.  That’s what I do for my Open Jump DB Query plugin.

Assuming your table is called “province”, and has a geometry column called “geom”, here’s how to query the database and load the resulting geometry:

// Do JDBC work to load driver, create connection, statement, etc.
// Then....

String query = "SELECT geom FROM province";
ResultSet resultSet = statement.executeQuery(query);

while(resultSet.next()) {

    //MySQL geometries are returned in JDBC as binary streams.  The
    //stream will be null if the record has no geometry.
    InputStream inputStream = resultSet.getBinaryStream("geom");
    Geometry geometry = getGeometryFromInputStream(inputStream);

    // do something with geometry...
}

Here’s the code that does the actual InputStream to Geometry conversion:


    private Geometry getGeometryFromInputStream(InputStream inputStream) throws Exception {

         Geometry dbGeometry = null;

         if (inputStream != null) {

             //convert the stream to a byte[] array
             //so it can be passed to the WKBReader
             byte[] buffer = new byte[255];

             int bytesRead = 0;
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             while ((bytesRead = inputStream.read(buffer)) != -1) {
                 baos.write(buffer, 0, bytesRead);
             }

             byte[] geometryAsBytes = baos.toByteArray();

             if (geometryAsBytes.length < 5) {
                 throw new Exception("Invalid geometry inputStream - less than five bytes");
             }

             //first four bytes of the geometry are the SRID,
             //followed by the actual WKB.  Determine the SRID
             //here
             byte[] sridBytes = new byte[4];
             System.arraycopy(geometryAsBytes, 0, sridBytes, 0, 4);
             boolean bigEndian = (geometryAsBytes[4] == 0x00);

             int srid = 0;
             if (bigEndian) {
                for (int i = 0; i < sridBytes.length; i++) {
                   srid = (srid << 8) + (sridBytes[i] & 0xff);
                }
             } else {
                for (int i = 0; i < sridBytes.length; i++) {
                  srid += (sridBytes[i] & 0xff) << (8 * i);
                }
             }

             //use the JTS WKBReader for WKB parsing
             WKBReader wkbReader = new WKBReader();

             //copy the byte array, removing the first four
             //SRID bytes
             byte[] wkb = new byte[geometryAsBytes.length - 4];
             System.arraycopy(geometryAsBytes, 4, wkb, 0, wkb.length);
             dbGeometry = wkbReader.read(wkb);
             dbGeometry.setSRID(srid);
         }

         return dbGeometry;
     }

And that’s it.  Pretty easy thanks to JTS!

8 thoughts on “Loading MySQL Spatial Data with JDBC and JTS WKBReader

  1. Dharma

    In the following line

    srid = (value << + (sridBytes[i] & 0xff);

    Can you please tell me what is “value”?..
    I guess there is a compile time error here. Please help me on this

    Reply
  2. lreeder Post author

    WordPress was converting some of the java syntax to emoticons. The value should have been:

    srid = (value << 8) + (sridBytes[i] & 0xff); Fixed in the post above. Thanks for the heads-up! -Larry

    Reply
  3. Dharma

    I’m sorry but still the value of the variable “value” is not initialized with anything .. By value do you mean the parameter “inputstream”?..

    Reply
  4. lreeder Post author

    You’re right.

    “value” should be “srid”

    This came from a bad cut and paste. I fixed and compiled my example this time, so hopefully it finally works for you as well.

    Reply
  5. Brian

    There’s no reason to make an entire copy of the byte[] to trim the first four bytes off it… WKBReader has an option that takes an input stream, which could be a ByteArrayInputStream wrapped around the byte[] then advanced by four.

    Reply
  6. Pingback: Reading Geometry (spatial data) from MySql | siddharth's space

  7. fb

    Thanks a lot! I’ve been struggling with the srid (i. e. trying to parse
    the byte[] directly with WKBReader –> no reasonable output…).
    You really saved my day!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *