Blog Zenika

#CodeTheWorld

Java

MySQL Spatial et JDBC

MySQL possède quelques fonctionnalités géographiques: un type de données “Geometry”, quelques fonctions sur ce type et un type d’index spatial (R-Tree). La version 5.6 apporte un lot de fonctions supplémentaires dont l’objectif est le support de la norme OpenGIS For SQL.
Comment manipuler ce type de données SQL en Java? Contrairement à PostgreSQL ou Oracle, pour lesquelles les drivers JDBC contiennent des objets PGgeometry ou JGeometry, MySQL ne propose rien.

Pour palier à ce manque, on peut s’appuyer sur JTS (Java Topology Suite). JTS contient les classes nécessaires pour décrire des points, lignes brisées et surfaces.
Nous verrons dans cet article comment lire/écrire des géométries depuis du code Java.

Préparation du modèle

Pour commencer, on crée une table avec une colonne de type GEOMETRY:

    create table VILLE (
        ID_VILLE int not null primary key,
        GEOM geometry not null
    ) ENGINE=MyISAM;

Seul le storage engine MyISAM supporte les index spatiaux; InnoDB supporte seulement les colonnes géométriques. On est donc contraint de faire un choix: InnoDB ou index spatiaux.

    create spatial index IDX_VILLE_GEOM
    on VILLE(GEOM);

Une fois la table et l’index spatial créés voyons comment écrire dedans.

Conversion WKB/WKT

La norme OpenGIS SQL définit 2 formats pour décrire et échanger des géométries: un format textuel, le WKT (Well Known Text), et un format binaire, le WKB (Well Known Binary).
La première possibilité est d’utiliser le format WKT comme format pivot entre le monde Java et MySQL. Pour une écriture:

  insert into VILLE(ID_VILLE, GEOM) values (?, GeomFromText(?));
  Geometry geometry = new Point(...);
  String wkt = new WKTWriter().write(geometry);
  preparedStatement.setString(2, wkt);

On procède de même pour une lecture:

  select AsWKT(GEOM) from VILLE where ID_VILLE=?
  String wkt = resultSet.getString(1);
  Geometry geometry = new WKTReader().read(wkt);

On peut appliquer cette même stratégie avec WKB:

En SQL: GeomFromText et AsWKT deviennent GeomFromBinary et AsWKB

En Java: WKTReader et WKTWriter deviennent WKBReader et WKBWriter

Format interne

Une autre stratégie est de passer directement par le format interne MySQL. On manipule alors les colonnes de type GEOMETRY comme des BLOBs. La structure binaire de ce type de colonne est:

  • 4 octets (soit un entier 32 bits): le SRID (référentiel spatial dans lequel sont exprimées les coordonnées)
  • Tout le reste du flux: la géometrie au format binaire WKB

Du coup pour les écritures:

  insert into VILLE(ID_VILLE, GEOM) values (?, ?);
  int byteOrder = ByteOrderValues.LITTLE_ENDIAN;
  // SRID
  ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  byte[] sridBytes = new byte[4];
  ByteOrderValues.putInt(userObject.getSRID(), sridBytes, byteOrder);
  outputStream.write(sridBytes);
  // Geometry
  WKBWriter wkbWriter = new WKBWriter(2, byteOrder);
  wkbWriter.write(geometry, new OutputStreamOutStream(outputStream));
  byte[] bytes = outputStream.toByteArray();
  preparedStatement.setBytes(2, bytes);

Et pour les lectures:

  select GEOM from VILLE where ID_VILLE=?
  int byteOrder = ByteOrderValues.LITTLE_ENDIAN;
  byte[] bytes = resultSet.getBytes(1);
  ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
  // SRID
  byte[] sridBytes = new byte[4];
  inputStream.read(sridBytes);
  int srid = ByteOrderValues.getInt(sridBytes, byteOrder);
  // Geometry
  GeometryFactory geometryFactory = new GeometryFactory(precisionModel, srid, coordinateSequenceFactory);
  WKBReader wkbReader = new WKBReader(geometryFactory);
  Geometry geometry = wkbReader.read(new InputStreamInStream(inputStream));

Le code Java de cette variante est plus compliqué, mais on s’évite les conversions dans le code SQL. Correctement enveloppé dans notre framework JDBC favori (TypeHandler dans MyBatis, Converter dans jOOQ, DataType dans DBUnit), cette stratégie simplifie le SQL.
Les heureux utilisateurs d’Hibernate n’auront rien à faire, puisque Hibernate Spatial fait déjà tout le boulot (en utilisant le format interne).

Auteur/Autrice

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.