Skip to Content

Jython et le géospatial: exemples avec GeoScript (GeoTools) , JTS ( Java Topology Suite), OpenJump, gvSIG, WorldWind ou Geoserver


python

Tout le monde connait le Python standard (ou « C Python »), qui s'exécute dans un environnement C, mais, peu connaissent ou utilisent ses autres implémentations (voir «  Python: géospatial, dialectes (standard, pour ESRI, pour FME, pour GvSIG etc... ») .

Parmi celles-ci, il y a Jython, qui est son implémentation en Java et qui s'exécute dans une Machine Virtuelle Java (JVM). Dans le monde géospatial, il est utilisé comme langage de script pour les SIGs écrits en Java, comme gvSIG ou OpenJump et pour un serveur cartographique, GeoServer.

Mais, tout comme Python, Jython peut-être utilisé directement pour traiter les données géospatiales. Il peut utiliser tous les modules Python écrits en Python pur, mais, comme déjà signalé, il sera impossible d'utiliser des modules écrits en C (GDAL, numpy,...). En contrepartie, Jython pourra utiliser toutes les classes des librairies Java existantes. Ses caractéristiques sont les suivantes (fr.wikipedia.org/wiki/Jython):

  • le code Python est compilé en bytecode Java ;
  • les classes Python peuvent hériter des classes Java;
  • possibilité d'exécuter des codes Python durant le fonctionnement d’un programme Java (utilisée par GvSIG ou OpenJump) ;
  • utilisation d’objets Java dans le code Python.

Dans Jython, la variable CLASSPATH est fondamentale, car c'est elle qui permet l'utilisation de n'importe quelle classe Java. Ainsi si je veux utiliser le driver JDBC natif (postgresql-xxx.jdbc3.jar) pour me connecter à  PostgreSQL  (voir www.chicoree.fr/w/Utiliser_JDBC_%C3%A0_partir_de_Jython):

connexion

  1. # ajustement du CLASSPATH de Jython pour l'importation des classes du driver .jar
  2. $ export CLASSPATH="/Library/Java/Extensions/postgresql-9.0-801.jdbc3.jar"
  3.  
  4. $ jython
  5. # importation des classes (première utilisation, après cela ne sera plus signalé)
  6. *sys-package-mgr*: processing new jar, '/Library/Java/Extensions/postgresql-9.0-801.jdbc3.jar'
  7.  
  8. Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06)
  9. [Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_24
  10. Type "help", "copyright", "credits" or "license" for more information.
  11. >>> from org.postgresql import Driver
  12. >>>
  13. # si aucune erreur n'est signalée, c'est que ça marche

Je n'irai pas plus loin dans la présentation de Jython (installation sur les diverses plates-formes , principes, etc.) renvoyant les lecteurs à des sites plus spécialisés comme opikanoba.org/java/java-et-python (en français, mais, il y en a beaucoup d'autres). Pour débuter, il y a un livre présenté en Open Source avec une licence Creative Commons 3.0. , « The Definitive Guide to Jython » (www.jython.org/jythonbook/en/1.0/), tous les livres et articles présentés à wiki.python.org/jython/JythonBibliography, des blogs ou des forums comme le  Forum des développeurs (www.developpez.net/forums/). 

Je me limiterai ici  à son application dans le domaine géospatial et dans la suite, nous traiterons:

  • l'utilisation de Jython seul avec des modules géospatiaux (GeoScript, JTS);
  • l'utilisation de Jython comme langage de script de logiciels SIG (OpenJump, GvSIG, Worldwind;
  • l'utilisation de Jython avec un serveur cartographique (GeoServer)

Utilisation de Jython seul

GeoScript ou l'équivalent de Shapely pour Jython

Shapely, dépendant de la librairie C GEOS, ne peut donc pas être utilisé, mais, il  a son équivalent pour Jython, GeoScript, proposé par GeoTools  « The Open Source Java GIS Toolkit » qui est un produit de l'OSGeo. GeoScript permet d'utiliser les classes de GeoTools en Jython, JavaScript, Scala et Groovy.

Comment l'installer:

La manière la plus simple est d'installer easy_install (c'est possible depuis les versions 2.5.x de Jython). Pour ce faire, il faut télécharger le script peak.telecommunity.com/dist/ez_setup.py et ensuite:

jython ez_setup.py

Ensuite, après téléchargement et décompression de GeoScript (geoscript.org/py/quickstart.html#quickstart)

jython setup.py install (easy_install va vous installer toutes les dépendances)

Ce qui va vous créer (entre autres choses) deux fichier dans le dossier bin du dossier Jython2.5.x, geoscript et geoscript-classpath

Utilisation

Il y a 2 manières d'utiliser GeoScript:

  • soit à partir de l'application geoscript

    avec geoscript

    1. $ geoscript
    2. Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06)
    3. [Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_24
    4. Type "help", "copyright", "credits" or "license" for more information.
    5. >>>
  • soit à partir de jython lui-même

    avec jython

    1. # ajustement du CLASSPATH
    2. export CLASSPATH=`/.../jython2.5.2/bin/geoscript-classpath`
    3. $ jython
    4. Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06)
    5. [Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_24
    6. Type "help", "copyright", "credits" or "license" for more information.
    7. >>> import geoscript
    8. >>>
    9.  
    10. # si vous ne le faites pas
    11. $ jython
    12. Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06)
    13. [Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_24
    14. Type "help", "copyright", "credits" or "license" for more information.
    15. >>> import geoscript
    16. Error: Could not find GeoTools libraries on classpath.

Ensuite, c'est pratiquement comme avec le module Shapely (voir www.portailsig.org/content/python-le-module-shapely-geometries-predicats-spatiaux-analyse-spatiale-matrices-de-clementi)

traitement

  1. >>> from geoscript import geom
  2. >>> point = geom.readWKT('POINT(45 20)')
  3. >>> point
  4. POINT (45 20)
  5. # ou
  6. >>> point = geom.Point(4.607817,50.664930)
  7. >>> point
  8. POINT (4.607817 50.66493)
  9. >>> # ou
  10. >>> wkt = str(point)
  11. >>> wkt
  12. 'POINT (4.607817 50.66493)'

Mais, en plus, GeoScript permet:

  • les changements de système de projection:

    projections

    1. >>> from geoscript import proj
    2. >>> point = geom.Point(4.607817,50.664930)
    3. >>> point2 = proj.transform(point, 'epsg:4326', 'epsg:31370')
    4. >>> point2
    5. POINT (166901.5939415752 150507.1768997945)
  • l'importation directe des shapefiles et leur création (et des couches PostGIS):

    Shapefiles

    1. >>> from geoscript.layer import Shapefile
    2. >>> macouche = Shapefile('.../montest.shp')
    3. >>> from geoscript.layer import Shapefile
    4. >>> pntGeoms = [f.geom for f in macouche.features()]
    5. >>> pntGeoms
    6. [MULTILINESTRING ((0.0316820276497696 0.6129032258064516, 0.1157834101382488 0.7177419354838709, 0.2217741935483871 0.683179723502304, 0.2839861751152074 0.5656682027649769, 0.3680875576036867 0.6048387096774194, 0.4326036866359447 0.7085253456221199, 0.5293778801843319 0.6071428571428571)), MULTILINESTRING ((0.3162442396313365 0.8940092165898617, 0.2321428571428572 0.8421658986175116, 0.2459677419354839 0.7039170506912441, 0.1756912442396313 0.5794930875576036, 0.2736175115207373 0.4331797235023041))]
  • leur représentation graphique (directement par Java):

    plot

    1. >>> from geoscript.viewer import plot
    2. >>> plot(pntGeoms)
  • etc. (voir geoscript.org/py/index.html ou  geoscriptblog.blogspot.com/ pour de nombreux exemples)

avec la librairie Java,  JTS - Java Topology Suite

La Java Topology Suite est la librairie Java pour le traitement des géométries en 2 dimensions. La librairie C GEOS en est dérivée. Elle est utilisée par GeoTools, GeoServer, tous les Jump's, GvSIG ou Udig par exemple (voir tsusiatsoftware.net/jts/jts-links.html#systems).  GeoScript est basée sur cette librairie, mais toutes ses classes n'ont pas encore été portées.

JTS ne possède pas d'implémentation Python, mais nous avons vu que Jython permet d'employer n'importe quelle classe Java. Alors certains ont agi...

Jython et JTS

  1. >>> import sys
  2. >>> # autre manière d'importer des classes (ici JTS)
  3. >>> sys.path.append('.../jts-1.8.0/lib/jts-1.8.jar')
  4. >>> # test d'une classe de JTS
  5. >>> from com.vividsolutions.jts.geom import Point
  6. >>>

Le problème est de trouver comment appeler les classes Java de JTS en Jython. Cela est réalisé par certains à coup d'essais et d'erreurs. Il est cependant nécessaire de bien connaitre la bibliothèque Java au préalable. C'est le cas de Carl Trachte sur son site geoscriptblog.blogspot.com/2010/06/unwrapped-jts-with-python.html, qui offre de très belles applications.

résultat d'une triangulation de Delaunay avec JTS

Utilisation de Jython comme langage de script de logiciels SIG

Jython peut aussi, en théorie, être utilisé comme langage de script dans des applications Java comme gvSig, OpenJump (et la grande famille des Jump's, voir www.portailsig.org/content/sortie-de-la-version-151-de-openjump-gis-et-catalogue-de-tous-les-logiciels-derives-de-jump) ou Worldwind Java. Pourquoi en théorie ? Parce que son utilisation nécessite une bonne connaissance des classes Java de l'application et donc de Java et qu'il y a très peu d'exemples disponibles.

avec OpenJump (et la famille des Jump's)

Nous commençons par OpenJump car son module Jython est l'application directe de l'utilisation de la Java Topology Suite (JTS).

Malheureusement, bien que très puissant, il faut « chipoter » pour trouver comment ce module fonctionne. Il n'y a ni tutoriel ni exemples. J'y suis arrivé en analysant systématiquement les fonctions des objets obtenus et la connaissance de la manière de fonctionner des modules Python dans le domaine geospatial. Comme cet aspect est mal connu pour OpenJump, une petite synthèse des résultats obtenus:

traitements avec le module Jython d'OpenJump

  1. # importation du module principal d'OpenJump
  2. from org.openjump.util.python.JUMP_GIS_Framework import *
  3. # obtention des couches sélectionnées
  4. couches = getSelectedLayers()
  5. # fonctions disponibles pour les couches sélectionnées:
  6. dir(couches)
  7. ['add', 'addAll', 'class', 'clear', 'clone', 'contains', 'containsAll', 'empty', 'ensureCapacity', 'equals', 'get', 'getClass', 'hashCode', 'indexOf', 'isEmpty', 'iterator', 'lastIndexOf', 'listIterator', 'notify', 'notifyAll', 'remove', 'removeAll', 'retainAll', 'set', 'size', 'subList', 'toArray', 'toString', 'trimToSize', 'wait']
  8.  
  9. # itération à travers les couches sélectionnées
  10. for i in couches.iterator():
  11. print couche.name
  12. ...
  13. # fonctions disponibles pour une couche:
  14. dir(couche)
  15. ['FIRING_APPEARANCE_CHANGED_ON_ATTRIBUTE_CHANGE', 'addStyle', 'addUndo', 'basicStyle', 'blackboard', 'class', 'cloneStyles', 'dataSourceQuery', 'defaultLineColor', 'description', 'dispose', 'drawingLast', 'editable', 'equals', 'featureCollection', 'featureCollectionModified', 'featureCollectionWrapper', 'fireAppearanceChanged', 'fireLayerChanged', 'getBasicStyle', 'getBlackboard', 'getClass', 'getDataSourceQuery', 'getDescription', 'getFeatureCollectionWrapper', 'getLabelStyle', 'getLayerManager', 'getMaxScale', 'getMinScale', 'getName', 'getStyle', 'getStyles', 'getTask', 'getVertexStyle', 'hasReadableDataSource', 'hashCode', 'isDrawingLast', 'isEditable', 'isFeatureCollectionModified', 'isReadonly', 'isScaleDependentRenderingEnabled', 'isSelectable', 'isSynchronizingLineColor', 'isVisible', 'labelStyle', 'layerManager', 'maxScale', 'minScale', 'name', 'notify', 'notifyAll', 'readonly', 'removeStyle', 'scaleDependentRenderingEnabled', 'selectable', 'setDataSourceQuery', 'setDescription', 'setDrawingLast', 'setEditable', 'setFeatureCollection', 'setFeatureCollectionModified', 'setLayerManager', 'setMaxScale', 'setMinScale', 'setName', 'setReadonly', 'setScaleDependentRenderingEnabled', 'setSelectable', 'setStyles', 'setSynchronizingLineColor', 'setVisible', 'styles', 'synchronizingLineColor', 'task', 'toString', 'tryToInvalidateEnvelope', 'vertexStyle', 'visible', 'wait']
  16.  
  17. ### Applications ###
  18. # itération à travers les géométries d'une couche
  19. fc = couches[0].featureCollectionWrapper
  20. for elem in range(fc.size):
  21. obj = fc.features[elem]
  22. geom = obj.getGeometry()
  23. ...
  24.  
  25. # fonctions disponibles pour la géométrie d'un élément (la 3D est reconnue par défaut):
  26. dir(geom)
  27. ['SRID', 'apply', 'area', 'boundary', 'boundaryDimension', 'buffer', 'centroid', 'class', 'clone', 'compareTo', 'contains', 'convexHull', 'coordinate', 'coordinates', 'coveredBy', 'covers', 'crosses', 'difference', 'dimension', 'disjoint', 'distance', 'empty', 'envelope', 'envelopeInternal', 'equals', 'equalsExact', 'equalsNorm', 'equalsTopo', 'exteriorRing', 'factory', 'geometryChanged', 'geometryType', 'getArea', 'getBoundary', 'getBoundaryDimension', 'getCentroid', 'getClass', 'getCoordinate', 'getCoordinates', 'getDimension', 'getEnvelope', 'getEnvelopeInternal', 'getExteriorRing', 'getFactory', 'getGeometryN', 'getGeometryType', 'getInteriorPoint', 'getInteriorRingN', 'getLength', 'getNumGeometries', 'getNumInteriorRing', 'getNumPoints', 'getPrecisionModel', 'getSRID', 'getUserData', 'hashCode', 'interiorPoint', 'intersection', 'intersects', 'isEmpty', 'isRectangle', 'isSimple', 'isValid', 'isWithinDistance', 'length', 'norm', 'normalize', 'notify', 'notifyAll', 'numGeometries', 'numInteriorRing', 'numPoints', 'overlaps', 'precisionModel', 'rectangle', 'relate', 'reverse', 'setSRID', 'setUserData', 'simple', 'symDifference', 'toString', 'toText', 'touches', 'union', 'userData', 'valid', 'wait', 'within']
  28. # d'où :
  29. geom.geometryType
  30. 'Polygon'
  31. # la géométrie
  32. geom.envelope
  33. POLYGON ((226023.7 18173, 226023.7 28173.5, 234026.3 28173.5, 234026.3 18173, 226023.7 18173))
  34. # nombre de points
  35. geom.getNumPoints()
  36. 5
  37. # les coordonnées des points de la géométrie
  38. geom.coordinates
  39. array(com.vividsolutions.jts.geom.Coordinate,[(226023.7, 28173.5, NaN), (234025.0, 28173.5, NaN), (234026.3, 18173.1, NaN), (226024.5, 18173.0, NaN), (226023.7, 28173.5, NaN)])
  40. geom.coordinate
  41. (226023.7, 28173.5, NaN)
  42. # le centroide
  43. geom.getCentroid()
  44. POINT (230024.86833801787 23173.222923277397)
  45. # la limite extérieure de la géométrie
  46. geom.getBoundaryDimension()
  47. 1
  48. geom.getBoundary()
  49. LINEARRING (226023.7 28173.5, 234025 28173.5, 234026.3 18173.1, 226024.5 18173, 226023.7 28173.5)
  50. # ou
  51. geom.exteriorRing
  52. LINEARRING (226023.7 28173.5, 234025 28173.5, 234026.3 18173.1, 226024.5 18173, 226023.7 28173.5)
  53.  
  54. # les attributs
  55. fonctions disponibles pour les attributs d'un élément:
  56. >>> dir(obj)
  57. ['ID', 'attributes', 'class', 'clone', 'compare', 'compareTo', 'equals', 'geometry', 'getAttribute', 'getAttributes', 'getClass', 'getDouble', 'getGeometry', 'getID', 'getInteger', 'getSchema', 'getString', 'hashCode', 'isModified', 'modified', 'notify', 'notifyAll', 'schema', 'setAttribute', 'setAttributes', 'setGeometry', 'setModified', 'setSchema', 'toString', 'wait']
  58.  
  59. # d'
  60. obj.attributes
  61. array(java.lang.Object,[POLYGON ((226023.7 28173.5, 234025 28173.5, 234026.3 18173.1, 226024.5 18173, 226023.7 28173.5)), '71/5', '71', '225W', 'LAMORTEAU', 'LAMORTEAU - RUETTE', '71/5-6'])
  62. # on retrouve donc la géométrie
  63. obj.getGeometry()
  64. POLYGON ((226023.7 28173.5, 234025 28173.5, 234026.3 18173.1, 226024.5 18173, 226023.7 28173.5))
  65. # ou
  66. obj.getAttribute(0)
  67. POLYGON ((226023.7 28173.5, 234025 28173.5, 234026.3 18173.1, 226024.5 18173, 226023.7 28173.5))
  68. # attributs éventuels (fonction de la liste obtenue)
  69. obj.getAttribute(1)
  70. '71/5'
  71. obj.getString(3)
  72. '225W'

Et donc, il y a moyen d'utiliser directement toutes les fonctions de la JTS:

Il est aussi possible de remarquer que bien qu'OpenJump ne gère pas les projections, son module Jython permet théoriquement de le faire (SRID).

Il a aussi moyen de créer des extensions/modules en Jython (voir mekandizim.mersin.edu.tr/belgeler/SSA-OJ.pdf, par exemple, et son module disponible à mekandizim.mersin.edu.tr/eklentiler/eklentiler.html)

avec gvSIG:

Le traitement est comparable à celui d'OpenJump mais il sera moins développé ici du fait d'un changement de module avec la future version 2.0 en développement.

from gvsig import *
from commonsdialog import *
def main():
  layer = currentLayer()
  if layer == None:
    msgbox("Vous devez choisir une couche!", "AVIS", 1)
    return
  emax = 0.0
  emin = 0.0
  for feature in layer.features():
    if feature.ELEVATION > emax :
      emax = feature.ELEVATION
    if feature.ELEVATION < emin or emin ==0.0:
      emin = feature.ELEVATION
  msgbox("Altitude maximum=%s et minimum=%s" % (emax, emin), "Elevation", 0)

Résultat:

image reprise de blog.gvsig.org/2012/10/22/scripting-exprime-tu-gvsig/

Il est aussi possible de créer des extensions/modules en Jython (voir mekandizim.mersin.edu.tr/belgeler/book.pdf et la même référence que pour OpenJump)

avec Worldwind Java

Je n'ai trouvé qu'un seul exemple d'utilisation à www.perrygeo.net/wordpress/.

Utlisation de Jython avec un serveur cartographique: GeoServer

GeoServer a aussi lancé son module Jython (geoserver.org/display/GEOS/Python+Scripting+Extension). Encore expérimental, il permet d'interroger Geoserver et même d'interagir avec lui.

from geoserver import Catalog
cat = Catalog('aff_geol')
print cat.stores()
[u'affleurements', u'cartes']
st = cat['affleurements']
print st.layers()
[u'affs']
l = st['affs']
print l.count()
3214
print l.bounds()
(107206.74,148102.45,114110.46,157514.40, EPSG:31370)

Conclusions

Tant qu'il s'agit de modules « clés en main »,  comme GeoScript, Jython est facile à utiliser, mais dès que l'on veut utiliser directement une librairie Java, une bonne connaissance de ce langage s'impose. Le même constat s'adresse aussi aux langages de scripts des SIGs.

Le nouveau module gvsig de la version 2 de gvSIG parait néanmoins se rapprocher beaucoup plus d'une version Python (sans devoir connaître le détail des classes Java dont il dépend). Et finalement j'ai été fort impressionné par les possibilités du module d'OpenJump (logiciel à mon avis sous-évalué).

Je regrette toujours qu'il ne soit pas possible d'utiliser tout en même temps, à savoir PyQGIS dans gvSIG et l'inverse ou les deux dans un shell, par exemple....

Mais, en pratique, connaître Python + Java, le tout sans un tutoriel détaillé, rend son utilisation quelque peu difficile pour le commun des mortels ...

Tous les traitements ont été effectués sur Mac OS X avec Jython 2.5.2, GeoScript version 1.1, OpenJump version 1.5.1 rev.2721 et diverses implémentations de gvSIG.


Site officiel : Jython
Site officiel : GeoTools (OSGeo)
Site officiel : GeoScript
Site officiel : Java Topology Suite (VIVID Solutions)
Site officiel : JTS Topology Suite
Site officiel : JTS (Sourceforge)
Autres Liens : Python: géospatial, dialectes (standard, pour ESRI, pour FME, pour GvSIG etc.) et incompréhensions...


Creative Commons License
licence Creative Commons Paternité-Pas d'Utilisation Commerciale-Partage des Conditions Initiales à l'Identique Commerciale 2.0 France

Commentaires

Poster un nouveau commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.