geotools学习(二)要素

要素教程

欢迎

您应该在运行本工作簿之前完成GeoTools的快速入门。我们需要确保您有一个使用GeoTools jar及其所有依赖项的环境。对于那些只使用maven的,我们将在每个部分开始时提供所需的依赖项
本工作簿采用了一种新的“代码优先”方法。我们已经尽了一切努力使这些例子既可视化又以代码为中心。我们已经包括了一些背景材料,解释了概念和想法,如果你感兴趣

CSV2SHP

我们今年正在尝试引入新功能;我们将从从头开始构建一个shapefile开始,这样您就可以看到创建特性的每一个细节,而不是阅读一个shapefile并在一个人工练习中将其分解。

教程包含以下内容:
    1.创建一个FeatureType, FeatureCollection 和Features
    2.使用GeometryFactory来构建点
    3.输出一个shapefile文件
    4.投影

在本教程的最后,您将能够创建自己的自定义shapefile。

csv数据

1.创建一个txt文件并拷贝下面数据进去

LAT, LON, CITY, NUMBER
46.066667, 11.116667, Trento, 140
44.9441, -93.0852, St Paul, 125
13.752222, 100.493889, Bangkok, 150
45.420833, -75.69, Ottawa, 200
44.9801, -93.251867, Minneapolis, 350
46.519833, 6.6335, Lausanne, 560
48.428611, -123.365556, Victoria, 721
-33.925278, 18.423889, Cape Town, 550
-33.859972, 151.211111, Sydney, 436
41.383333, 2.183333, Barcelona, 914
39.739167, -104.984722, Denver, 869
52.95, -1.133333, Nottingham, 800
45.52, -122.681944, Portland, 840
37.5667,129.681944,Seoul,473
50.733992,7.099814,Bonn,700,2016

2.或者下载 locations.csv.
3.添加一些其他的点,如你的家乡

依赖

请确保您的pom.xml包含以下内容:


  
   org.geotools
   gt-shapefile
   ${geotools.version}
  
  
   org.geotools
   gt-epsg-hsql
   ${geotools.version}
  
 
  
      
          maven2-repository.dev.java.net
          Java.net repository
          http://download.java.net/maven/2
      
      
          osgeo
          Open Source Geospatial Foundation Repository
          http://download.osgeo.org/webdav/geotools/
      
      
        
          true
        
        boundless
        Boundless Maven Repository
        http://repo.boundlessgeo.com/main
      
  

注意,上面提到的jar将引入大量其他依赖项(例如hsql数据库驱动程序)

主程序

1.请创建一个包org.geotools.tutorial.feature 和类 Csv2Shape.java .
2.粘贴复制下面代码

package org.geotools.tutorial.feature;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.UIManager;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

/**
 * This example reads data for point locations and associated attributes from a comma separated text
 * (CSV) file and exports them as a new shapefile. It illustrates how to build a feature type.
 *
 * 

Note: to keep things simple in the code below the input file should not have additional spaces * or tabs between fields. */ public class Csv2Shape { public static void main(String[] args) throws Exception { // Set cross-platform look & feel for compatability UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); File file = JFileDataStoreChooser.showOpenFile("csv", null); if (file == null) { return; }

现在,我们将在小节中查看主要方法的其余部分

创建一个FeatureType

根据csv中的数据创建一个FeatureType并写入shapefile
这是DataUtilities 类

 /*
         * We use the DataUtilities class to create a FeatureType that will describe the data in our
         * shapefile.
         *
         * See also the createFeatureType method below for another, more flexible approach.
         */
        final SimpleFeatureType TYPE =
                DataUtilities.createType(
                        "Location",
                        "the_geom:Point:srid=4326,"
                                + // <- the geometry attribute: Point type
                                "name:String,"
                                + // <- a String attribute
                                "number:Integer" // a number attribute
                        );
        System.out.println("TYPE:" + TYPE);
创建features

我们现在可以读取CSV文件并为每个记录创建一个特性。请注意以下事项:
1.使用GeometryFactory创建新点
2.使用SimpleFeatureBuilder创建特性(SimpleFeature对象)

  * A list to collect features as we create them.
         */
        List features = new ArrayList<>();

        /*
         * GeometryFactory will be used to create the geometry attribute of each feature,
         * using a Point object for the location.
         */
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();

        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            /* First line of the data file is the header */
            String line = reader.readLine();
            System.out.println("Header: " + line);

            for (line = reader.readLine(); line != null; line = reader.readLine()) {
                if (line.trim().length() > 0) { // skip blank lines
                    String tokens[] = line.split("\\,");

                    double latitude = Double.parseDouble(tokens[0]);
                    double longitude = Double.parseDouble(tokens[1]);
                    String name = tokens[2].trim();
                    int number = Integer.parseInt(tokens[3].trim());

                    /* Longitude (= x coord) first ! */
                    Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));

                    featureBuilder.add(point);
                    featureBuilder.add(name);
                    featureBuilder.add(number);
                    SimpleFeature feature = featureBuilder.buildFeature(null);
                    features.add(feature);
                }
            }
        }
从要素集中创建一个shapefile

注意点:
1.使用DataStoreFactory和一个参数来表示我们需要一个空间索引
2.使用createSchema(SimpleFeatureType)方法来设置shapefile(我们将在下一节中创建getNewShapeFile方法)

  /*
         * Get an output file name and create the new shapefile
         */
        File newFile = getNewShapeFile(file);

        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

        Map params = new HashMap<>();
        params.put("url", newFile.toURI().toURL());
        params.put("create spatial index", Boolean.TRUE);

        ShapefileDataStore newDataStore =
                (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);

        /*
         * TYPE is used as a template to describe the file contents
         */
        newDataStore.createSchema(TYPE);
把要素写入shapefile文件中

注意事项:
1.通过确认FeatureSource对象实现了FeatureStore方法,我们可以检查是否具有读写访问权限
2.花点时间检查一下shapefile与我们的模板(SimpleFeatureType类型)的匹配程度。比较这个输出,看看它们有什么不同
3.我们使用的SimpleFeatureStore需要一个FeatureCollection 对象,因此我们将特性列表包装在ListFeatureCollection中
4.使用transaction.commit()可以一次安全地写出这些特性

   /*
         * Write the features to the shapefile
         */
        Transaction transaction = new DefaultTransaction("create");

        String typeName = newDataStore.getTypeNames()[0];
        SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
        SimpleFeatureType SHAPE_TYPE = featureSource.getSchema();
        /*
         * The Shapefile format has a couple limitations:
         * - "the_geom" is always first, and used for the geometry attribute name
         * - "the_geom" must be of type Point, MultiPoint, MuiltiLineString, MultiPolygon
         * - Attribute names are limited in length
         * - Not all data types are supported (example Timestamp represented as Date)
         *
         * Each data store has different limitations so check the resulting SimpleFeatureType.
         */
        System.out.println("SHAPE:" + SHAPE_TYPE);

        if (featureSource instanceof SimpleFeatureStore) {
            SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
            /*
             * SimpleFeatureStore has a method to add features from a
             * SimpleFeatureCollection object, so we use the ListFeatureCollection
             * class to wrap our list of features.
             */
            SimpleFeatureCollection collection = new ListFeatureCollection(TYPE, features);
            featureStore.setTransaction(transaction);
            try {
                featureStore.addFeatures(collection);
                transaction.commit();
            } catch (Exception problem) {
                problem.printStackTrace();
                transaction.rollback();
            } finally {
                transaction.close();
            }
            System.exit(0); // success!
        } else {
            System.out.println(typeName + " does not support read/write access");
            System.exit(1);
        }
    }

这就完成了主方法。

提示输出shapefile

这个方法提示用户选择shapefile输出位置,文件名为csv的文件名

/**
     * Prompt the user for the name and path to use for the output shapefile
     *
     * @param csvFile the input csv file used to create a default shapefile name
     * @return name and path for the shapefile as a new File object
     */
    private static File getNewShapeFile(File csvFile) {
        String path = csvFile.getAbsolutePath();
        String newPath = path.substring(0, path.length() - 4) + ".shp";

        JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
        chooser.setDialogTitle("Save shapefile");
        chooser.setSelectedFile(new File(newPath));

        int returnVal = chooser.showSaveDialog(null);

        if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {
            // the user cancelled the dialog
            System.exit(0);
        }

        File newFile = chooser.getSelectedFile();
        if (newFile.equals(csvFile)) {
            System.out.println("Error: cannot replace " + csvFile);
            System.exit(0);
        }

        return newFile;
    }
运行程序

当你运行程序时,会提醒你:
1.选择csv文件的位置
2.选择输出的shp文件的位置

另外尝试

另一种构建SimpleFeatureType的方法
尽管上面使用的DataUtilities 类提供了一种快速且简单的方法来构建一个简单的featuretype。对于任何实际的应用程序,您都希望使用更灵活的SimpleFeatureTypeBuilder。
下面是如何使用SimpleFeatureTypeBuilder来完成相同的结果:

   /**
     * Here is how you can use a SimpleFeatureType builder to create the schema for your shapefile
     * dynamically.
     *
     * 

This method is an improvement on the code used in the main method above (where we used * DataUtilities.createFeatureType) because we can set a Coordinate Reference System for the * FeatureType and a a maximum field length for the 'name' field dddd */ private static SimpleFeatureType createFeatureType() { SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.setName("Location"); builder.setCRS(DefaultGeographicCRS.WGS84); // <- Coordinate reference system // add attributes in order builder.add("the_geom", Point.class); builder.length(15).add("Name", String.class); // <- 15 chars width for name field builder.add("number", Integer.class); // build the type final SimpleFeatureType LOCATION = builder.buildFeatureType(); return LOCATION; }

注意使用大写常量来保存SimpleFeatureType。因为SimpleFeatureType类是不可变的,所以将它们作为最终变量进行跟踪可以帮助您记住,一旦创建它们,就不能修改它们。
使用这个方法,我们的SimpleFeatureType包含一个CoordinateReferenceSystem,因此不需要调用forceSchemaCRS来生成.prj文件。另外,我们现在将Name字段限制为15个字符。

其他尝试的

1.修改代码从数据文件头读取特征属性名,而不是在应用程序中硬编码:

LAT, LON, CITY, NUMBER

您应该能够使用SimpleFeatureTypeBuilder。
2.使用几何缓冲区方法创建基于每个城市的人口规模的圆圈

Polygon polygon = location.buffer( 0.1 );

3.:编写一个快速的CSVReader很容易,就像我们在这里所做的;但要写出一篇能够正确处理引号的好文章就比较难了。JavaCSV是一个开放源码库,用于读取带有各种选项的CSV文件
4.为了快速找到相关依赖,我们可以访问 http://mvnrepository.com/.
这样的站点将直接提供一个maven依赖项,您可以将其剪切并粘贴到pom.xml中。


  net.sourceforge.javacsv
  javacsv
  2.0

5.使用相同的技术从其他结构化文件格式(如GeoJson)的数据创建shapefile
6.地球刚刚经历了一场流星风暴——在全球范围内产生了100个不同大小的圆。你的城市看到了吗?
从模型或分析中生成一个shapefile是一种常见的用法。
7.读一下shapefile支持的其他几何类:线性特征的MultiLineString和区域特征的MultiPolygon,然后修改这个例子来使用它们

Feature

Feature是可以画在地图上的东西。严格的定义是,一个特征是现实世界中的东西——风景的特征——珠穆朗玛峰,埃菲尔铁塔,甚至是像你的大姨妈爱丽丝那样四处走动的东西。
向Java开发人员解释这个概念很容易——特性就是对象。

与java对象一样,特性可以包含一些关于它们所代表的真实世界的信息。这些信息被组织到属性中,就像Java信息被组织到字段中一样。

时你有两个有很多共同点的特征。洛杉矶的LAX机场和悉尼的SYD机场。因为这两个特性有一些共同点,所以最好将它们组合在一起——在Java中,我们将创建一个名为Airport的类。在地图上,我们将创建一个名为Airport的功能类型。

尽管Java不支持它,但早期的编程语言使用了一个原型系统(而不是类系统),它支持许多“一次性”对象。你会发现这种情况在绘制地图时相当普遍——因为埃菲尔铁塔有多少座?你偶尔也会发现相同的现实世界的事物以不同的方式表现出来(埃菲尔铁塔可以是一个地标,也可以是一座塔,这取决于背景)。

这里有一个方便的备忘单:


image.png

特性模型实际上比我们Java程序员习惯的要疯狂一点,因为它认为属性和操作都是特性的“属性”。也许,当Java获得闭包时,我们就可以迎头赶上了

对我来说,真正有趣的事情是,地图制作者们早在15世纪就开始整理这些东西了,而且他们也变得像现在的程序员一样极客。因此,尽管我们很乐意教他们关于面向对象编程的知识,但他们已经有了丰富的思想来描述这个世界。好的一面是,地图制作者开始使用UML图。

FeatureClass

在GeoTools中,我们有一个由GeoAPI项目提供的特性、特性类型和属性的接口。一般来说,GeoAPI提供了一个非常严格的接口,而GeoTools将提供一个类。


image.png

一个Feature 通常只有简单的属性(字符串、整数、日期等),而没有对其他特性或数据结构的引用,比如List。满足这一需求的Features 是如此常见,以至于我们创建了一个子类来表示它们,这个子类叫做SimpleFeature。

在Java级别,GeoTools提供的特性API与Java .util类似。使用Map——它是一个用于保存信息的Java数据结构。您可以通过键查找属性值;键的列表是由FeatureType提供的。

Geometry

feature和object的另一个区别是,feature具有某种形式的位置信息(如果没有,我们就不能在地图上绘制它)。位置信息将由存储在属性中的几何图形(或形状)捕获。

image.png

我们利用JTS拓扑套件(JTS)来表示几何图形。JTS库提供了一种优秀的几何图形实现——并且因为拥有递归首字母缩写而获得极客的分数!JTS是一个令人惊奇的库,它提供了所有的硬图理论,让您以高效的方式处理几何。

下面是一个使用文本(WKT)格式创建点的示例

GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory( null );

WKTReader reader = new WKTReader( geometryFactory );
Point point = (Point) reader.read("POINT (1 1)");

您还可以直接使用GeometryFactory手动创建一个点。

GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory( null );

Coordinate coord = new Coordinate( 1, 1 );
Point point = geometryFactory.createPoint( coord );

DataStore

我们在《快速起步》中已经遇到了数据存储。DataStore API用于表示包含空间数据的文件、数据库或服务。该API有两个移动部分,如下所示。


image.png

FeatureSource用于读取features,而FeatureStore子类用于读写访问。

判断一个文件是否可以在GeoTools中写入的方法是使用instanceof 检查

String typeNames = dataStore.getTypeNames()[0];
SimpleFeatureSource source = store.getfeatureSource( typeName );
if( source instanceof SimpleFeatureStore){
   SimpleFeatureStore store = (SimpleFeatureStore) source; // write access!
   store.addFeatures( featureCollection );
   store.removeFeatures( filter ); // filter is like SQL WHERE
   store.modifyFeature( attribute, value, filter );
}

我们决定将写访问作为一个子类(而不是isWritable方法)来处理,以避免方法被使用。

你可能感兴趣的:(geotools学习(二)要素)