1. Información general
En este artículo, repasaremos los conceptos básicos de la biblioteca Java de código abierto de GeoTools para trabajar con datos geoespaciales . Esta biblioteca proporciona métodos compatibles para implementar sistemas de información geográfica (GIS) e implementa y admite muchos estándares del Open Geospatial Consortium (OGC).
A medida que el OGC desarrolla nuevos estándares, los GeoTools los implementan, lo que lo hace muy útil para el trabajo geoespacial.
2. Dependencias
Necesitaremos agregar las dependencias de GeoTools a nuestro archivo pom.xml . Dado que estas dependencias no están alojadas en Maven Central, también necesitamos declarar sus repositorios para que Maven pueda descargarlas:
osgeo Open Source Geospatial Foundation Repository //download.osgeo.org/webdav/geotools/ opengeo OpenGeo Maven Repository //repo.opengeo.org
Después de eso, podemos agregar nuestras dependencias:
org.geotools gt-shapefile 15.2 org.geotools gt-epsg-hsql 15.2
3. GIS y Shapefiles
Para tener algún uso práctico de la biblioteca GeoTools, necesitaremos saber algunas cosas sobre los sistemas de información geográfica y los shapefiles .
3.1. GIS
Si queremos trabajar con datos geográficos, necesitaremos un sistema de información geográfica (SIG). Este sistema se puede utilizar para presentar, capturar, almacenar, manipular, analizar o gestionar datos geográficos .
Una parte de los datos geográficos es espacial: hace referencia a ubicaciones concretas en la tierra. Los datos espaciales suelen ir acompañados de los datos de atributos. Los datos de atributos pueden ser cualquier información adicional sobre cada una de las características espaciales.
Un ejemplo de datos geográficos serían las ciudades. La ubicación real de las ciudades son los datos espaciales. Los datos adicionales, como el nombre de la ciudad y la población, compondrían los datos de atributos.
3.2. Shapefiles
Hay diferentes formatos disponibles para trabajar con datos geoespaciales. El ráster y el vector son los dos tipos de datos principales.
En este artículo, vamos a ver la forma de trabajo con los datos vectoriales typ correo . Este tipo de datos se puede representar como puntos, líneas o polígonos.
Para almacenar datos vectoriales en un archivo, usaremos un shapefile . Este formato de archivo se utiliza cuando se trabaja con el tipo de datos vectoriales geoespaciales. Además, es compatible con una amplia gama de software GIS.
Podemos usar GeoTools para agregar características como ciudades, escuelas y puntos de referencia a los shapefiles .
4. Creación de funciones
La documentación de GeoTools especifica que una característica es cualquier cosa que se pueda dibujar en un mapa, como una ciudad o algún punto de referencia. Y, como mencionamos, una vez creadas, las características se pueden guardar en archivos llamados shapefiles .
4.1. Conservación de datos geoespaciales
Antes de crear una característica, necesitamos conocer sus datos geoespaciales o las coordenadas de longitud y latitud de su ubicación en la Tierra. En cuanto a los datos de atributos, necesitamos saber el nombre de la característica que queremos crear.
Esta información se puede encontrar en la web. Algunos sitios como simplemaps.com o maxmind.com ofrecen bases de datos gratuitas con datos geoespaciales.
Cuando conocemos la longitud y latitud de una ciudad, podemos almacenarlas fácilmente en algún objeto. Podemos usar un objeto Mapa que contendrá el nombre de la ciudad y una lista de sus coordenadas.
Creemos un método auxiliar para facilitar el almacenamiento de datos dentro de nuestro objeto Map :
private static void addToLocationMap( String name, double lat, double lng, Map
locations) { List coordinates = new ArrayList(); coordinates.add(lat); coordinates.add(lng); locations.put(name, coordinates); }
Ahora completemos nuestro objeto Map :
Map
locations = new HashMap(); addToLocationMap("Bangkok", 13.752222, 100.493889, locations); addToLocationMap("New York", 53.083333, -0.15, locations); addToLocationMap("Cape Town", -33.925278, 18.423889, locations); addToLocationMap("Sydney", -33.859972, 151.211111, locations); addToLocationMap("Ottawa", 45.420833, -75.69, locations); addToLocationMap("Cairo", 30.07708, 31.285909, locations);
Si descargamos alguna base de datos CSV que contenga estos datos, podemos crear fácilmente un lector para recuperar los datos en lugar de guardarlos en un objeto como este.
4.2. Definición de tipos de características
Entonces, ahora tenemos un mapa de ciudades. Para poder crear entidades con estos datos, primero tendremos que definir su tipo. GeoTools ofrece dos formas de definir tipos de características.
Una forma es utilizar el método createType de la clase DataUtilites :
SimpleFeatureType TYPE = DataUtilities.createType( "Location", "location:Point:srid=4326," + "name:String");
Otra forma es utilizar SimpleFeatureTypeBuilder , que proporciona más flexibilidad . Por ejemplo, podemos establecer el Sistema de referencia de coordenadas para el tipo y podemos establecer una longitud máxima para el campo de nombre:
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.setName("Location"); builder.setCRS(DefaultGeographicCRS.WGS84); builder .add("Location", Point.class); .length(15) .add("Name", String.class); SimpleFeatureType CITY = builder.buildFeatureType();
Ambos tipos almacenan la misma información. La ubicación de la ciudad se almacena como un Punto y el nombre de la ciudad se almacena como una Cadena .
Probablemente haya notado que las variables de tipo TYPE y CITY se nombran con letras mayúsculas, como constantes. Las variables de tipo deben tratarse como variables finales y no deben cambiarse después de su creación , por lo que esta forma de nombrar puede usarse para indicar solo eso.
4.3. Creación de funciones y colecciones de funciones
Una vez que tenemos definido el tipo de característica y tenemos un objeto que tiene los datos necesarios para crear características, podemos comenzar a crearlas con su constructor.
Creemos una instancia de SimpleFeatureBuilder que proporcione nuestro tipo de característica:
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(CITY);
También necesitaremos una colección para almacenar todos los objetos de características creados:
DefaultFeatureCollection collection = new DefaultFeatureCollection();
Since we declared in our feature type to hold a Point for the location, we'll need to create points for our cities based on their coordinates. We can do this with the GeoTools's JTSGeometryFactoryFinder:
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
Note that we can also use other Geometry classes like Line and Polygon.
We can create a function that will help us put features in the collection:
private static Function
, SimpleFeature> toFeature(SimpleFeatureType CITY, GeometryFactory geometryFactory) { return location -> { Point point = geometryFactory.createPoint( new Coordinate(location.getValue() .get(0), location.getValue().get(1))); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(CITY); featureBuilder.add(point); featureBuilder.add(location.getKey()); return featureBuilder.buildFeature(null); }; }
Once we have the builder and the collection, by using the previously created function, we can create features and store them in our collection:
locations.entrySet().stream() .map(toFeature(CITY, geometryFactory)) .forEach(collection::add);
The collection now contains all the features created based on our Map object that held the geospatial data.
5. Creating a DataStore
GeoTools contains a DataStore API that is used to represent a source of geospatial data. This source can be a file, a database, or some service that returns data. We can use a DataStoreFactory to create our DataStore, which will contain our features.
Let's set the file that will contain the features:
File shapeFile = new File( new File(".").getAbsolutePath() + "shapefile.shp");
Now, let's set the parameters that we are going to use to tell the DataStoreFactory which file to use and indicate that we need to store a spatial index when we create our DataStore:
Map params = new HashMap(); params.put("url", shapeFile.toURI().toURL()); params.put("create spatial index", Boolean.TRUE);
Let's create the DataStoreFactory using the parameters we just created, and use that factory to create the DataStore:
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory(); ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params); dataStore.createSchema(CITY);
6. Writing to a Shapefile
The last step that we need to do is to write our data to a shapefile. To do this safely, we are going to use the Transaction interface that is a part of the GeoTools API.
This interface gives us the possibility to easily commit our the changes to the file. It also provides a way to perform a rollback of the unsuccessful changes if some problem occurs while writing to the file:
Transaction transaction = new DefaultTransaction("create"); String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); if (featureSource instanceof SimpleFeatureStore) { SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; featureStore.setTransaction(transaction); try { featureStore.addFeatures(collection); transaction.commit(); } catch (Exception problem) { transaction.rollback(); } finally { transaction.close(); } }
The SimpleFeatureSource is used to read features, and the SimpleFeatureStore is used for read/write access. It is specified in the GeoTools documentation that using the instanceof method for checking if we can write to the file is the right way to do so.
This shapefile can later be opened with any GIS viewer that has shapefile support.
7. Conclusion
In this article, we saw how we can make use of the GeoTools library to do some very interesting geo-spatial work.
Although the example was simple, it can be extended and used for creating rich shapefiles for various purposes.
Debemos tener en cuenta que GeoTools es una biblioteca vibrante, y este artículo solo sirve como una introducción básica a la biblioteca. Además, GeoTools no se limita a crear tipos de datos vectoriales únicamente; también se puede usar para crear o trabajar con tipos de datos ráster.
Puede encontrar el código de ejemplo completo utilizado en este artículo en nuestro proyecto GitHub. Este es un proyecto de Maven, por lo que debería poder importarlo y ejecutarlo como está.