1. What is GeoTools?
Geotools is a java library that provides a number of standard classes and methods for working with spatial data, and is built on top of the OGC standard, which is an implementation of the OGC idea. OGC is an international standard, so geotools will become the main tool for open source spatial data processing in the future, and most of the current open source software, such as udig, geoserver, etc., are supported by geotools for spatial data processing. Many other web services, command-line tools, and desktop programs can be implemented by GeoTools.
Key features:
- Geotools mainly provides a variety of GIS algorithms to realize the reading, writing and display of various data formats.
- In terms of display, it is a little worse, but the simple view and operation of the map are realized with Swing.
- Users can visualize the map by themselves according to the algorithm provided by Geotools. OpenJump and udig are based on Geotools.
- At present, most of the open source software, such as UDIG, GeoServer, etc., is supported by GeoTools for the processing of spatial data.
- Web services, command-line tools, and desktop programs can all be implemented by GeoTools.
- It is built on the OGC standard and is an implementation of the OGC idea. OGC is an international standard, so geotools will definitely become the main tool for open-source spatial data processing in the future.
- The two most important open source GIS toolkits used by Geotools are JTS and GeoAPI. The former is mainly to implement various GIS topology algorithms [only the nine-intersection model of graphics and graphics is not a topological algorithm between layers or between layers], and it is also based on GeoAPI.
- Geotools is still only based on 2D graphics and lacks support for 3D spatial data algorithms and displays.
Data formats supported by Geotools
- arcsde, arcgrid, geotiff, grassraster, gtopo30, image(JPEG, TIFF, GIF, PNG), imageio-ext-gdal, imagemoasaic, imagepyramid, JP2K,matlab;
- Supported databases “jdbc-ng”:d B2, H2, MySQL, Oracle, PostGIS, Spatialite, SQLServ;
- Supported vector formats and data access: app-schema, arcsde, csv, dxf, edigeo, excel, geojson,org, property, shapefile, wfs;
- XML bindings. The xml-based Java data structures and bindings provide the following formats: xsd-core (xml simple types), fes, filter, gml2, gml3, kml, ows, sld, wcs, wfs, wms, wps, vpf. For additional geometry, SSL and filter encoding and parsing can be done via DOM and SAX programs.
Most OGC standards are supported
- SLD/SE and rendering engine in OGC;
- The OGC general element model includes simple element support;
- Grid image representation of raster information in OGC;
- WFS, WMS and additional WPS in OGC;
- ISO 19107 geometry specification;
Open-source projects that Geotools relies on
- JTS: JTS is an open-source Java API from Vivid Solutions in Canada. It provides a set of core algorithms for spatial data manipulation, and provides a 2D spatial predicate API for basic geometric manipulation in OGC-compliant spatial object models.
- GeoAPI: GeoAPI provides a set of Java interfaces for the OpenGIS specification.
GeoTools orientation
2. Code engineering
Objective: To implement geotools to read .shp files
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>GeoTools</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<geotools.version>20.0</geotools.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>${geotools.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>osgeo-snapshot</id>
<name>OSGeo Snapshot Repository</name>
<url>https://repo.osgeo.org/repository/snapshot/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>
controller
package com.et.geotools.controller;
import com.et.geotools.pojos.ShpInfo;
import com.et.geotools.result.ResponseResult;
import com.et.geotools.service.ShpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
@CrossOrigin
@RequestMapping("/shper")
@RestController
public class ShpController {
@Autowired
private ShpService shpService;
@GetMapping("/hello")
public String sayHello(){
return "Hello Appleyk's Controller !";
}
@PostMapping("/write")
public ResponseResult write(@RequestBody ShpInfo shpInfo) throws Exception{
return shpService.writeShp(shpInfo);
}
@GetMapping("/query")
public ResponseResult query(@RequestParam(value = "path",required = true) String shpFilePath,
@RequestParam(value = "limit",required = false,defaultValue = "10") Integer limit ) throws Exception{
return shpService.getShpDatas(shpFilePath,limit);
}
@GetMapping("/show")
public void show(@RequestParam(value = "path",required = true) String path,
@RequestParam(value = "imagePath",required = false) String imagePath,
@RequestParam(value = "color",required = false) String color,
HttpServletResponse response) throws Exception{
response.setContentType("image/png");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
shpService.showShp(path, imagePath,color ,response);
}
}
service
package com.et.geotools.service;
import com.et.geotools.geotools.ShpTools;
import com.et.geotools.pojos.ShpDatas;
import com.et.geotools.pojos.ShpInfo;
import com.et.geotools.result.ResponseMessage;
import com.et.geotools.result.ResponseResult;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
@Service
public class ShpService {
public ResponseResult getShpDatas(String shpPath, Integer limit) throws Exception{
ShpDatas shpDatas = ShpTools.readShpByPath(shpPath, limit);
return new ResponseResult(ResponseMessage.OK,shpDatas);
}
public void showShp(String shpPath,String imagePath,String color, HttpServletResponse response) throws Exception{
ShpTools.shp2Image(shpPath, imagePath ,color,response);
}
public ResponseResult writeShp(ShpInfo shpInfo) throws Exception{
return ShpTools.writeShpByGeom(shpInfo);
}
}
util
package com.et.geotools.geotools;
import com.et.geotools.IO.StringTokenReader;
import com.et.geotools.pojos.ShpDatas;
import com.et.geotools.pojos.ShpInfo;
import com.et.geotools.result.ResponseMessage;
import com.et.geotools.result.ResponseResult;
import org.geotools.data.*;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.swing.JMapFrame;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.*;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.springframework.util.ResourceUtils;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class ShpTools {
private static GeometryCreator gCreator = GeometryCreator.getInstance();
private static ReferencedEnvelope bounds;
private static final int IMAGE_WIDTH = 1280;
private static final int IMAGE_HEIGHT = 1200;
public static ShpDatas readShpByPath(String filePath, Integer limit) throws Exception {
ShapefileDataStore shpDataStore = new ShapefileDataStore(new File(filePath).toURI().toURL());
shpDataStore.setCharset(StandardCharsets.UTF_8);
String typeName = shpDataStore.getTypeNames()[0];
System.out.println("shp name:"+typeName);
FeatureCollection<SimpleFeatureType, SimpleFeature> result = getFeatures(shpDataStore, typeName);
FeatureIterator<SimpleFeature> iterator = result.features();
ShpDatas shpDatas = new ShpDatas();
shpDatas.setName(typeName);
shpDatas.setShpPath(filePath);
buildShpDatas(limit, iterator, shpDatas);
iterator.close();
return shpDatas;
}
private static FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatures(ShapefileDataStore shpDataStore, String typeName) throws IOException {
FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = shpDataStore.getFeatureSource(typeName);
FeatureCollection<SimpleFeatureType, SimpleFeature> result = featureSource.getFeatures();
System.out.println("record:"+result.size()+" ");
System.out.println("==================================");
return result;
}
private static void buildShpDatas(Integer limit, FeatureIterator<SimpleFeature> iterator, ShpDatas shpDatas) {
int stop = 0;
while (iterator.hasNext()) {
if (stop > limit) {
break;
}
SimpleFeature feature = iterator.next();
Collection<Property> p = feature.getProperties();
Map<String,Object> prop = new HashMap<>();
for (Property pro : p) {
String key = pro.getName().toString();
String val;
if ("java.util.Date".equals(pro.getType().getBinding().getName())){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
val = pro.getValue() ==null ? "" : dateFormat.format(pro.getValue());
}else{
val = pro.getValue()==null ?"":pro.getValue().toString();
}
prop.put(key, val);
System.out.println("key【field】:"+key+"\t||value【value】:"+val);
}
System.out.println("\n============================ num:"+stop+"\n");
shpDatas.addProp(prop);
stop++;
}
}
public static void writeShpByGeom(String filePath, Geometry geometry) throws Exception{
ShapefileDataStore ds = getshpDS(filePath, geometry);
FeatureWriter<SimpleFeatureType, SimpleFeature> writer = ds.getFeatureWriter(ds.getTypeNames()[0],
Transaction.AUTO_COMMIT);
SimpleFeature feature = writer.next();
feature.setAttribute("name", "XXXX");
feature.setAttribute("path", "c:/test");
feature.setAttribute("the_geom", geometry);
feature.setAttribute("id", 1010L);
feature.setAttribute("des", "XXXX");
writer.write();
writer.close();
ds.dispose();
}
public static ResponseResult writeShpByGeom(ShpInfo shpInfo) throws Exception{
StringTokenReader reader = new StringTokenReader();
Geometry geometry = reader.read(shpInfo.getGeom());
String path = shpInfo.getPath();
File file = new File(path);
if(!file.exists()){
file.mkdir();
}
if(!file.isDirectory()){
return new ResponseResult(ResponseMessage.BAD_REQUEST,"path不是有效的文件夹" );
}
String filePath = shpInfo.getPath()+"/"+shpInfo.getName()+".shp";
ShapefileDataStore ds = getshpDS(filePath, geometry);
String typeName = ds.getTypeNames()[0];
FeatureWriter<SimpleFeatureType, SimpleFeature> writer ;
if(shpInfo.isAppendWrite()){
writer = ds.getFeatureWriterAppend(typeName, Transaction.AUTO_COMMIT);
}else{
writer = ds.getFeatureWriter(typeName, Transaction.AUTO_COMMIT);
}
SimpleFeature feature = writer.next();
feature.setAttribute("name", shpInfo.getName());
feature.setAttribute("path", shpInfo.getPath());
feature.setAttribute("the_geom", geometry);
feature.setAttribute("id", shpInfo.getId());
feature.setAttribute("des", shpInfo.getDes());
System.out.println("========= 写入【"+geometry.getGeometryType()+"】成功 !=========");
writer.write();
writer.close();
ds.dispose();
return new ResponseResult(ResponseMessage.OK,filePath);
}
private static ShapefileDataStore getshpDS(String filePath, Geometry geometry) throws IOException {
File file = new File(filePath);
Map<String, Serializable> params = new HashMap<>();
params.put(ShapefileDataStoreFactory.URLP.key, file.toURI().toURL());
ShapefileDataStore ds = (ShapefileDataStore) new ShapefileDataStoreFactory().createNewDataStore(params);
SimpleFeatureTypeBuilder tBuilder = new SimpleFeatureTypeBuilder();
tBuilder.setCRS(DefaultGeographicCRS.WGS84);
tBuilder.setName("shapefile");
tBuilder.add("name", String.class);
tBuilder.add("path", String.class);
tBuilder.add("the_geom", geometry.getClass());
tBuilder.add("id", Long.class);
tBuilder.add("des", String.class);
ds.createSchema(tBuilder.buildFeatureType());
ds.setCharset(StandardCharsets.UTF_8);
return ds;
}
public static MapContent getMapContentByPath(String filePath,boolean isOpenByChoose,String color) throws Exception{
File file;
if(isOpenByChoose){
file = JFileDataStoreChooser.showOpenFile("shp", null);
}else{
file = new File(filePath);
}
if(file==null){
return null;
}
FileDataStore store = FileDataStoreFinder.getDataStore(file);
((ShapefileDataStore)store).setCharset(Charset.forName("UTF-8"));
SimpleFeatureSource featureSource = store.getFeatureSource();
bounds = featureSource.getBounds();
MapContent mapContent = new MapContent();
mapContent.setTitle("Appleyk's GeoTools");
Color color1;
if(color == null || "".equals(color.toLowerCase())){
color1 = Color.BLACK;
}else if("red".equals(color.toLowerCase())){
color1 = Color.RED;
}else if("green".equals(color.toLowerCase())){
color1 = Color.GREEN;
}else if("blue".equals(color.toLowerCase())){
color1 = Color.BLUE;
}else{
color1 = Color.getColor(color);
}
Style style = SLD.createSimpleStyle(featureSource.getSchema(),color1);
Layer layer = new FeatureLayer(featureSource, style);
mapContent.addLayer(layer);
return mapContent;
}
public static void showMap(MapContent mapContent){
JMapFrame.showMap(mapContent);
}
public static void shp2Image(String shpFilePath,String destImagePath,String color, HttpServletResponse response) throws Exception{
StreamingRenderer renderer = new StreamingRenderer();
MapContent mapContent = getMapContentByPath(shpFilePath,false,color );
renderer.setMapContent(mapContent);
Rectangle imageBounds = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
BufferedImage dumpImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = dumpImage.createGraphics();
g2d.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
renderer.paint(g2d, imageBounds, bounds);
g2d.dispose();
if(destImagePath == null || "".equals(destImagePath)){
ImageIO.write(dumpImage, "png", response.getOutputStream());
}else{
ImageIO.write(dumpImage, "png", new File(destImagePath));
}
}
public static void main(String[] args) throws Exception{
File file = ResourceUtils.getFile("classpath:static/shpTest[Point]/dp_tl.shp");
readShpByPath(file.getAbsolutePath(),10);
String filePath = "C:/test/test.shp";
String pointWkt="POINT (120.76164848270959 31.22001141278534)";
Point point = gCreator.createPointByWKT(pointWkt);
String polygonWkt="POLYGON ((103.859188 34.695908, 103.85661 34.693788, 103.862027 34.69259, 103.863709 34.695078, 103.859188 34.695908))";
Polygon polygon = gCreator.createPolygonByWKT(polygonWkt);
String linestringWkt="LINESTRING(113.511315990174 41.7274734296674,113.51492087909 41.7284983348307,113.516079593384 41.727649586406,113.515907932007 41.7262243043929,113.514019656861 41.7247989907606,113.512131381714 41.7250872589898,113.51138036319 41.7256637915682,113.511315990174 41.7274734296674)";
LineString lineString = gCreator.createLineByWKT(linestringWkt);
String multiPolyWkt = "MULTIPOLYGON(((101.870371 25.19228,101.873633 25.188183,101.880564 25.184416,101.886808 25.186028,101.892043 25.189969,101.896592 25.190163,101.903716 25.190785,101.905454 25.193464,101.899897 25.196202,101.894146 25.197911,101.891657 25.19826,101.886078 25.197658,101.884211145538 25.2007060137013,101.88172564506 25.1949712942389,101.87874 25.199619,101.874641 25.200998,101.868547 25.202415,101.863741 25.202415,101.85887 25.202842,101.854557 25.202182,101.852604 25.199736,101.852282 25.19628,101.854492 25.194183,101.855608 25.192668,101.863698 25.192105,101.870371 25.19228)))";
MultiPolygon multiPolygon = gCreator.createMulPolygonByWKT(multiPolyWkt);
Envelope envelope = polygon.getEnvelopeInternal();
System.out.println(envelope);
writeShpByGeom(filePath,point);
}
}
application.properties
server.port=8081
server.servlet.session.timeout=10
server.tomcat.uri-encoding=utf8
The above are just some of the key codes, all of which can be found in the repositories below
Code repositories
3. Testing
Start the Spring Boot application