当前位置: 首页 > news >正文

GeoTools学习笔记

Feature要素:
例子:Csv2Shape.java
创建要素,先创建FeatureType,再创建Feature
根据FeatureCollection,可以创建shapefile

https://docs.geotools.org/latest/userguide/library/main/data.html

API详解:
https://docs.geotools.org/latest/userguide/tutorial/feature/csv2shp.html
要素类型FeatureType:
要素类型定义了要素是点、线还是面,有哪些属性;
创建要素类型,可以使用DataUtilities工具类创建要素类型,也可以用SimpleFeatureTypeBuilder创建要素类型,参考FeatureTypeUtil类代码;
类名:FeatureType
子类:SimpleFeatureType

要素Feature:
例子:Csv2Shape.java
要素是地图上的某一个事物,比如山川、河流、建筑,用点、线、面来表示;
类名:Feature
子类:SimpleFeature
要素集合类:FeatureCollection,子类:SimpleFeatureCollection、ListFeatureCollection
创建要素,也就是创建点、线、面,用GeometryFactory,参考FeatureUtil类代码,要先创建Geometry,然后构建要素;

要素资源类:FeatureSource,子类:SimpleFeatureStore

存储类:ShapefileDataStore

事物类:Transaction

要素类型是类,要素是对象。比如Airport类代表机场,没一个机场则是一个Airport的实例对象;属性和操作都是要素的properties;
要素是一个存储属性键值对类似Map的数据结构,要素类型提供key的列表;
要素和对象的区别是,要素中有位置信息。位置信息存储在要素的Geometry属性中;

几何Geometry:
几何就是地理信息系统的形状。
通常一个要素只有一个几何形状;
我们使用JTS拓扑套件(JTS)来表示几何。
创建几何点、线、面,可以使用WKT,GeometryFactory 创建,参考CoordinateSequenceUtil,CoordinateUtil,WktUtil
Point
Line
Polygon
2个几何之间的关系,参考JTS文档
几何允许我们处理地理信息系统的“信息”部分——我们可以用它来创建新的几何形状和测试几何形状之间的关系。
// Create Geometry using other Geometry
Geometry smoke = fire.buffer(10);
Geometry evacuate = cities.intersection(smoke);

// test important relationships
boolean onFire = me.intersects(fire);
boolean thatIntoYou = me.disjoint(you);
boolean run = you.isWithinDistance(fire, 2);

// relationships actually captured as a fancy
// String called an intersection matrix
//
IntersectionMatrix matrix = he.relate(you);
thatIntoYou = matrix.isDisjoint();

// it really is a fancy string; you can do
// pattern matching to sort out what the geometries
// being compared are up to
boolean disjoint = matrix.matches("FF*FF****");
disjoin不相交谓词有以下等价定义:
这两种几何形状没有任何共同之处
两个几何图形的DE-9IM相交矩阵是FF*FF****
(不相交是交点的反转)
建议您阅读JTS的javadocs,其中包含有用的定义。

数据存储DataStore:
DataStore,用于表示包含空间数据的文件、数据库或服务
FeatureSource,用于读取要素(父类)
FeatureStore,子类FeatureStore用于读/写访问(子类)
判断一个文件是否可以在GeoTools中写入的方法是使用instanceof检查。
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 );
}
简化几何,可以使用TopologyPreservingSimplifier 和DouglasPeuckerSimplifier ,参考JTS网站https://github.com/locationtech/jts/blob/master/USING.md

坐标参考系统Coordinate Reference System:
例子:CRSLab.java
3本来没有意义,3加了单位后才有意义;
坐标参考系统告诉我们这些点的意义;
它定义了所使用的轴——以及测量单位。
所以你可以用距离赤道的度数来测量纬度,用距离格林威治子午线的度数来测量经度。
或者x的单位是米,y的单位是米这对于计算距离和面积很方便。
它定义了世界的形状。不,不是所有的坐标参考系都想象出相同的世界形状。谷歌使用的CRS假设世界是一个完美的球体,而“EPSG:4326”使用的CRS有一个不同的形状——所以如果你把它们混在一起,你的数据将被画在错误的地方。
EPSG Codes:
EPSG:4326,EPSG Projection 4326 - WGS 84
CRS.decode("EPSG:4326");
DefaultGeographicCRS.WGS84;

EPSG: 3785,Popular Visualization CRS / Mercator
CRS.decode("EPSG:3785");

EPSG:3005,NAD83 / BC Albers
CRS.decode("EPSG:3005");
地图总是以纬度记录位置,然后是经度;也就是说,南北轴线第一,东西通道第二。当你在屏幕上快速绘制时,你会发现世界是横向的,因为坐标是“y/x”,在我看来,你需要在绘制之前交换它们。
CRSAuthorityFactory   factory = CRS.getAuthorityFactory(true);
CoordinateReferenceSystem crs = factory.createCoordinateReferenceSystem("EPSG:4326");

 System.setProperty("org.geotools.referencing.forceXY", "true");

从这个实验中要学习的一件重要的事情是,在两个coordinatereferencesystem之间创建MathTransform是多么容易。您可以使用MathTransform一次转换一个点;或者使用JTS实用程序类创建一个修改了点的几何体副本。
我们使用与CSV2SHAPE示例类似的步骤来导出shapefile。在本例中,我们使用FeatureIterator从现有的shapefile中读取内容;并使用FeatureWriter一次一个地写出内容。使用后请关闭这些物品。
CoordinateReferenceSystem dataCRS = schema.getCoordinateReferenceSystem();
        CoordinateReferenceSystem worldCRS = map.getCoordinateReferenceSystem();
        boolean lenient = true; // allow for some error due to different datums
        MathTransform transform = CRS.findMathTransform(dataCRS, worldCRS, lenient);
修复无效几何图形有很多技巧。一个简单的起点是使用geometry.buffer(0)。使用本技巧构建您自己的shapefile数据清理器

手工完成所有几何变换的另一种方法是请求所需投影中的数据。
这个版本的导出方法展示了如何使用Query对象来检索重新投影的特征,并将它们写入新的shapefile,而不是像上面那样“手工”转换特征。
Query query = new Query(typeName);
        query.setCoordinateSystemReproject(map.getCoordinateReferenceSystem());

        SimpleFeatureCollection featureCollection = featureSource.getFeatures(query);

        // And create a new Shapefile with the results
        DataStoreFactorySpi factory = new ShapefileDataStoreFactory();

        Map<String, Serializable> create = new HashMap<>();
        create.put("url", file.toURI().toURL());
        create.put("create spatial index", Boolean.TRUE);
        DataStore newDataStore = factory.createNewDataStore(create);

        newDataStore.createSchema(featureCollection.getSchema());
        Transaction transaction = new DefaultTransaction("Reproject");
        SimpleFeatureStore featureStore =
                (SimpleFeatureStore) newDataStore.getFeatureSource(typeName);
        featureStore.setTransaction(transaction);
        try {
            featureStore.addFeatures(featureCollection);
            transaction.commit();
            JOptionPane.showMessageDialog(
                    null,
                    "Export to shapefile complete",
                    "Export",
                    JOptionPane.INFORMATION_MESSAGE);
        } catch (Exception problem) {
            transaction.rollback();
            problem.printStackTrace();
            JOptionPane.showMessageDialog(
                    null, "Export to shapefile failed", "Export", JOptionPane.ERROR_MESSAGE);
        } finally {
            transaction.close();
        }
    }


Query:
例子: QueryLab.java 
Connect to DataStore:
在快速入门中,我们使用FileDataStoreFinder连接到一个特定的文件。这一次,我们将使用更通用的DataStoreFinder,它接受连接参数的映射。
注意,相同的代码可以用于连接到DataStoreFactorySpi(服务提供程序接口)参数指定的完全不同类型的数据存储。文件菜单操作使用ShapefileDataStoreFactory或PostgisNGDataStoreFactory的实例调用此方法。
JDataStoreWizard显示一个对话框,其中包含适合于shapefile或PostGIS数据库的输入字段。它需要比JFileDataStoreChooser多几行代码,JFileDataStoreChooser在快速入门中用于提示用户输入shapefile,但允许更大的控制。
Query:
Filter类似于SQL语句的where子句;定义被筛选出的每个要素需要满足的条件。
以下是我们显示所选功能的策略:
获取用户选择的特性类型名称,并从数据存储中检索相应的FeatureSource。
获取在文本字段中输入的查询条件,并使用CQL类创建一个Filter对象。
将Filter传递给getFeatures方法,该方法以featurecollection的形式返回与查询匹配的要素。使用featureSource.getFeatures(filter)获取要素数据。
为对话框的JTable创建一个FeatureCollectionTableModel。这个GeoTools类接受一个featurecollection,并检索每个特征的特征属性名称和数据。

通过使用Query数据结构,您可以更好地控制请求,允许您只选择所需的属性;控制返回的特征数量;并要求一些具体的处理步骤,如重新投影。
Filter:
例子:CqlFilter
要从FeatureSource请求信息,我们需要描述(或选择)我们想要返回的信息。我们为此使用的数据结构称为过滤器。
可以使用CQL语句创建Filter;
也可以使用CQL+WKT文本创建Filter;
include,查询所有要素
CNTRY_NAME = 'France',查询名称等于France的要素
POP_RANK >= 5
CNTRY_NAME = 'Australia' AND POP_RANK > 5
BBOX(the_geom, 110, -45, 155, -10),空间查询,这是一个边界框查询,将选择110 - 155°W, 10 - 45°S(澳大利亚周围的松散框)范围内的所有要素
注意:Shapefile的几何属性名总是称为the_geom,对于其他数据存储,我们需要查找几何属性的名称。
CQL语句创建Filter:
Filter filter = CQL.toFilter("POPULATION > 30000");//CQL
CQL+WKT创建Filter:
Filter pointInPolygon = CQL.toFilter("CONTAINS(THE_GEOM, POINT(1 2))");//CQL+WKT
Filter clickedOn = CQL.toFilter("BBOX(ATTR1, 151.12, 151.14, -33.5, -33.51)";
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
FilterFactory创建Filter:
Filter filter = ff.propertyGreaterThan(ff.property("POPULATION"), ff.literal(12));
https://docs.geotools.org/latest/userguide/tutorial/filter/query.html
Filter是一个接口,结构是固定的;
好消息是过滤器可以用新的函数扩展;我们的实现可以学习如何使用PropertyAccessors处理新类型的数据。
Expression表达式:
用表达式访问数据例子:
ff.property("POPULATION"); // expression used to access the attribute POPULATION from a feature
ff.literal(12);            // the number 12
用表达式调用函数例子:
CQL.toExpression("buffer(THE_GEOM)");
CQL.toExpression("strConcat(CITY_NAME, POPULATION)");
CQL.toExpression("distance(THE_GEOM, POINT(151.14,-33.51))");
比较:
 FilterFactory ff = CommonFactoryFinder.getFilterFactory();
        Filter filter;

        // the most common selection criteria is a simple equal test
        ff.equal(ff.property("land_use"), ff.literal("URBAN"));

        // You can also quickly test if a property has a value
        filter = ff.isNull(ff.property("approved"));

        // The usual array of property comparisons is supported
        // the comparison is based on the kind of data so both
        // numeric, date and string comparisons are supported.
        filter = ff.less(ff.property("depth"), ff.literal(300));
        filter = ff.lessOrEqual(ff.property("risk"), ff.literal(3.7));
        filter = ff.greater(ff.property("name"), ff.literal("Smith"));
        filter = ff.greaterOrEqual(ff.property("schedule"), ff.literal(new Date()));

        // PropertyIsBetween is a short inclusive test between two values
        filter = ff.between(ff.property("age"), ff.literal(20), ff.literal("29"));
        filter = ff.between(ff.property("group"), ff.literal("A"), ff.literal("D"));

        // In a similar fashion there is a short cut for notEqual
        filter = ff.notEqual(ff.property("type"), ff.literal("draft"));

        // pattern based "like" filter
        filter = ff.like(ff.property("code"), "2300%");
        // you can customise the wildcard characters used
        filter = ff.like(ff.property("code"), "2300?", "*", "?", "\\");
Null vs Nil是否为null,是否存在:
FilterFactory ff = CommonFactoryFinder.getFilterFactory();
        Filter filter;

        // previous example tested if approved equals "null"
        filter = ff.isNull(ff.property("approved"));

        // this example checks if approved exists at all
        filter = ff.isNil(ff.property("approved"), "no approval available");
是否区分大小写,默认为true,可设置:
FilterFactory ff = CommonFactoryFinder.getFilterFactory();

        // default is matchCase = true
        Filter filter = ff.equal(ff.property("state"), ff.literal("queensland"));

        // You can override this default with matchCase = false
        filter = ff.equal(ff.property("state"), ff.literal("new south wales"), false);

        // All property comparisons allow you to control case sensitivity
        Filter welcome = ff.greater(ff.property("zone"), ff.literal("danger"), false);
MatchAction过滤返回的多个属性值,默认MatchAction.ANY:
filter.getMatchAction()
MatchAction.ANY,如果任何可能的操作数组合的结果为真,则计算结果为真::
List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});

        Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ANY);
        System.out.println("Any: " + filter.evaluate(null)); // prints Any: true
MatchAction.ALL,如果所有可能的操作数组合都为真,则计算结果为真:
List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});

        Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ALL);
        System.out.println("All: " + filter.evaluate(null)); // prints All: false
MatchAction.ONE,如果恰好有一个可能的值组合为真,则计算结果为真:
List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});

        Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ONE);
        System.out.println("One: " + filter.evaluate(null)); // prints One: true

 Filter filter = ff.greater(ff.property("child/age"), ff.literal(12), true, MatchAction.ALL);
逻辑运算:
过滤器可以使用通常的与and、或or和非not的二进制逻辑组合。
// you can use *not* to invert the test; this is especially handy
        // with like filters (allowing you to select content that does not
        // match the provided pattern)
        filter = ff.not(ff.like(ff.property("code"), "230%"));

        // you can also combine filters to narrow the results returned
        filter =
                ff.and(
                        ff.greater(ff.property("rainfall"), ff.literal(70)),
                        ff.equal(ff.property("land_use"), ff.literal("urban"), false));

        filter =
                ff.or(
                        ff.equal(ff.property("code"), ff.literal("approved")),
                        ff.greater(ff.property("funding"), ff.literal(23000)));
INCLUDES and EXCLUDES包含和不包含:
Filter.INCLUDES:
集合中包含的所有内容。如果在查询中使用,将返回所有内容。
Filter.EXCLUDES:
不要包含任何内容。如果在查询中使用,将返回一个空集合。
这些值通常在其他数据结构中用作默认值——例如Query.getFilter()的默认值是Filter.INCLUDES。
这些都是静态常量,不需要构造函数:
 filter = Filter.INCLUDE; // no filter provided! include everything
 filter = Filter.EXCLUDE; // no filter provided! exclude everything
你可以在优化时检查这些值:
public void draw( Filter filter ){
   if( filter == Filter.EXCLUDES ) return; // draw nothing

   Query query = new Query( "roads", filter );
   FeatureCollection collection = store.getFeatureSource( "roads" ).getFeatures( filter );
   ...
}
但是要小心,因为很容易混淆。
if( filter == Filter.INCLUDES || filter.evaluate( feature ) ){
    System.out.println( "Selected "+ feature.getId();
}
if( filter == Filter.INCLUDES || filter.evaluate( feature ) ){
    System.out.println( "Selected "+ feature.getId();
}
Identifier标识符:
使用过滤器的另一个有趣的方法是在GIS意义上将其视为“选择”。在这种情况下,我们将直接匹配FeatureId,而不是评估属性。
最常见的测试是针对FeatureId:
FilterFactory ff = CommonFactoryFinder.getFilterFactory();

        Filter filter =
                ff.id(
                        ff.featureId("CITY.98734597823459687235"),
                        ff.featureId("CITY.98734592345235823474"));

从形式上看,这种风格的Id匹配不应该与传统的基于属性的评估(如边界框过滤器)混合使用。
你也可以使用Set<FeatureId>:
 FilterFactory ff = CommonFactoryFinder.getFilterFactory();

        Set<FeatureId> selected = new HashSet<>();
        selected.add(ff.featureId("CITY.98734597823459687235"));
        selected.add(ff.featureId("CITY.98734592345235823474"));

        Filter filter = ff.id(selected);
使用标识符的另一个地方是处理版本化信息时。在这种情况下,使用的ResourceId由一个fid和一个rid组成。
ResourceId可以用来查看版本信息:
FilterFactory ff = CommonFactoryFinder.getFilterFactory();
        Filter filter;

        // grab a specific revision
        filter = ff.id(ff.featureId("CITY.98734597823459687235", "A457"));

        // You can also use ResourceId to grab a specific revision
        filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version()));

        // grab the one before that
        filter =
                ff.id(
                        ff.resourceId(
                                "CITY.98734597823459687235", "A457", new Version(Action.PREVIOUS)));

        // grab the one after that
        filter =
                ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(Action.NEXT)));

        // grab the first one
        filter =
                ff.id(
                        ff.resourceId(
                                "CITY.98734597823459687235", "A457", new Version(Action.FIRST)));

        // grab the first one (ie index = 1 )
        filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(1)));

        // grab the twelfth record in the sequence (ie index = 12 )
        filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(12)));

        // Grab the entry close to Jan 1985
        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
        df.setTimeZone(TimeZone.getTimeZone("GMT"));
        filter =
                ff.id(
                        ff.resourceId(
                                "CITY.98734597823459687235",
                                "A457",
                                new Version(df.parse("1985-1-1"))));

        // Grab all the entries in the 1990s
        filter =
                ff.id(
                        ff.resourceId(
                                "CITY.98734597823459687235",
                                df.parse("1990-1-1"),
                                df.parse("2000-1-1")));
Spatial空间过滤:
https://docs.geotools.org/latest/userguide/library/main/filter.html
下面是一个快速示例,展示如何在边界框内请求功能:
FilterFactory ff = CommonFactoryFinder.getFilterFactory();
        ReferencedEnvelope bbox =
                new ReferencedEnvelope(x1, x2, y1, y2, DefaultGeographicCRS.WGS84);
        Filter filter = ff.bbox(ff.property("the_geom"), bbox);
Temporal时间过滤器:
gt-main模块提供了我们需要的一些实现类:
DefaultIntant:这是用于表示单个时间点的Instant的实现。
DefaultPeriod:这是Period的实现,用于表示时间范围
下面是一个例子,说明了它们的构造和使用时间过滤器:
// use the default implementations from gt-main

        DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        Date date1 = FORMAT.parse("2001-07-05T12:08:56.235-0700");
        Instant temporalInstant = new DefaultInstant(new DefaultPosition(date1));

        // Simple check if property is after provided temporal instant
        Filter after = ff.after(ff.property("date"), ff.literal(temporalInstant));//在指定时间之后

        // can also check of property is within a certain period
        Date date2 = FORMAT.parse("2001-07-04T12:08:56.235-0700");
        Instant temporalInstant2 = new DefaultInstant(new DefaultPosition(date2));
        Period period = new DefaultPeriod(temporalInstant, temporalInstant2);

        Filter within = ff.toverlaps(ff.property("constructed_date"), ff.literal(period));//在指定时间范围内
Expression表达式:
上面提到的许多过滤器都是作为两个(或更多)表达式之间的比较而呈现的。表达式用于访问保存在Feature(或POJO,或Record,或…)中的数据。
根据一个要素对表达式求值:
Object value = expression.evaluate( feature );
或者针对Java Bean,甚至Java .util. map:
Object value = expression.evaluate( bean );
开箱即用表达式是无类型的,并且会尽力将值转换为所需的类型。
为了自己做到这一点,你可以在脑海中计算一个特定类型的对象:
Integer number = expression.evaulate( feature, Integer.class );
作为转换的一个例子,下面是一个将String转换为Color的表达式:
Expression expr = ff.literal("#FF0000")
Color color = expr.evaluate( null, Color.class );
表达式非常有用,你会在GeoTools的很多地方看到它们。样式使用它们来选择要描绘的数据等等。
PropertyName:
PropertyName表达式用于从数据模型中提取信息。
最常见的用法是访问Feature属性。
FilterFactory ff = CommonFactoryFinder.getFilterFactory( GeoTools.getDefaultHints() );
Expression expr = ff.property("name");
Object value = expr.evaluate( feature ); // evaluate
if( value instanceof String){
    name = (String) value;
}
else {
    name = "(invalid name)";
}
你也可以将值指定为String,如果不能将值强制转换为String则返回null:
FilterFactory ff = CommonFactoryFinder.getFilterFactory( GeoTools.getDefaultHints() );
Expression expr = ff.property("name");
String name = expr.evaluate( feature, String ); // evaluate
if( name == null ){
    name = "(invalid name)";
}
X-Paths and Namespaces:
可以在过滤器中使用XPath表达式。这对于根据复杂特性评估嵌套属性特别有用。要计算XPath表达式,需要一个org.xml.sax.helpers.NamespaceSupport对象来将前缀与名称空间URI关联起来。
FilterFactory支持创建带有关联命名空间上下文信息的PropertyName表达式。
FilterFactory ff = CommonFactoryFinder.getFilterFactory( GeoTools.getDefaultHints() );
NamespaceSupport namespaceSupport = new NamespaceSupport();
namespaceSupport.declarePrefix("foo", "urn:cgi:xmlns:CGI:GeoSciML:2.0" );
Filter filter = ff.greater(ff.property("foo:city/foo:size",namespaceSupport),ff.literal(300000));
命名空间上下文信息可以从现有的PropertyName表达式中检索:
PropertyName propertyName = ff.property("foo:city/foo:size", namespaceSupport);
NamespaceSupport namespaceSupport2 = propertyName.getNamespaceContext();
 // now namespaceSupport2 == namespaceSupport !
当PropertyName表达式不包含或不支持Namespace上下文信息时,PropertyName. getnamespacecontext()将返回null。
Functions:
你可以使用FilterFactory创建函数:
FilterFactory ff = CommonFactoryFinder.getFilterFactory( GeoTools.getDefaultHints() );
PropertyName a = ff.property("testInteger");
Literal b = ff.literal( 1004.0 );
Function min = ff.function("min", a, b );
对于接受多个形参的函数,你需要使用Array:
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
PropertyName property = ff.property("name");
Literal search = ff.literal("foo");
Literal replace = ff.literal("bar");
Literal all = ff.literal( true );
Function f = ff.function("strReplace", new Expression[]{property,search,replace,all});
FilterVisitor:
对于像List这样的简单数据结构,java提供了For循环,允许您遍历列表元素。由于Filter数据结构形成了一个树,我们有一个不同的方法——传入一个对象(称为访问者),该对象在树中的每个元素上都被调用。
当使用XSLT处理遍历树时,对XML文档(也形成树)使用类似的方法。
FilterVisitor用于遍历过滤器数据结构。常见用途包括:
询问有关过滤器内容的问题
对Filter执行分析和优化(比如用“2”替换“1+1”)
转换过滤器(考虑搜索和替换)
所有这些活动都有一些共同点:
过滤器的内容需要检查
需要建立一个结果或答案
下面是一个快速代码示例,展示了如何使用访问器遍历数据结构:
// The visitor will be called on each object
// in your filter
class FindNames extends DefaultFilterVisitor {
    public Set<String> found = new HashSet<String>();
    /** We are only interested in property name expressions */
    public Object visit( PropertyName expression, Object data ) {
        found.add( expression.getPropertyName() );
        return found;
    }
}
// Pass the visitor to your filter to start the traversal
FindNames visitor = new FindNames();
filter.accept( visitor, null );
System.out.println("Property Names found "+visitor.found );
FilterCapabilities:
FilterCapabilities数据结构用于描述WebFeatureService的本地能力。我们还使用这个数据结构来描述不同JDBC数据存储处理数据库的能力。特别感兴趣的是所支持的functionname列表。
这种数据结构在日常的GeoTools工作中并不常用;它主要是那些实现对新的web或数据库服务的支持的人感兴趣的。

Query:
Query数据结构用于对返回的结果提供更细粒度的控制。
下面的查询将从一个特征资源“cities”中请求THE_GEOM和POPULATION:
Query query = new Query("cities", filter, new String[]{ "THE_GEOM", "POPULATION" });
FeatureCollection:
在使用FeatureIterator遍历featurecollection的内容时需要特别注意。FeatureIterator实际上会将数据从磁盘上流出来,我们需要记住在完成后关闭流。

Grid Coverage:
例子:ImageLab.java,WMSLab.java
对于ImageLab.java,我们将通过显示三波段全球卫星图像来添加栅格数据,并将其与shapefile中的国家边界叠加在一起。
网格覆盖是覆盖的一种特殊情况,其中所有的特征最终都是地球表面上的小矩形。

Parameters:
到目前为止,有一件事一直是个谜,那就是如何创建数据存储向导。向导是根据连接时所需参数的描述创建的。
观察每个输入文件的Parameter对象的使用。传递给Parameter构造函数的参数是:
key:
an identifier for the Parameter参数标识
type:
the class of the object that the Parameter refers to参数引用的对象类
title:
a title which the wizard will use to label the text field向导将用于标记文本字段的标签
description:
a brief description which the wizard will display below the text field向导将在文本字段下方显示的简短说明
metadata:
a Map containing additional data for the Parameter - in our case this is one or more file extensions.包含参数附加数据的Map -在我们的例子中,这是一个或多个文件扩展名

 Web Map Server (WMS):
另一个图像来源是Web地图服务器(WMS)
http://localhost:8080/geoserver/wms?bbox=-130,24,-66,50&styles=population&Format=image/png&request=GetMap&layers=topp:states&width=550&height=250&srs=EPSG:4326
获取WMS服务图层:
URL url = new URL("http://atlas.gc.ca/cgi-bin/atlaswms_en?VERSION=1.1.1&Request=GetCapabilities&Service=WMS");
WebMapServer wms = new WebMapServer(url);
WMSCapabilities capabilities = wms.getCapabilities();
// gets all the layers in a flat list, in the order they appear in
// the capabilities document (so the rootLayer is at index 0)
List layers = capabilities.getLayerList();
设置WMS不同参数:
GetMapRequest request = wms.createGetMapRequest();
request.setFormat("image/png");
request.setDimensions("583", "420"); //sets the dimensions to be returned from the server
request.setTransparent(true);
request.setSRS("EPSG:4326");
request.setBBox("-131.13151509433965,46.60532747661736,-117.61620566037737,56.34191403281659");
GetMapResponse response = (GetMapResponse) wms.issueRequest(request);
BufferedImage image = ImageIO.read(response.getInputStream());

Coverage Processor覆盖处理器:
例子:ImageTiler.java,例子中介绍了tif文件切片成贴图
本工作手册介绍了直接在覆盖对象上执行常见操作-例如多重,重新采样,裁剪等。
Image Tiling Application图形平铺应用:
CoverageProcessor


Style:
例子:StyleLab.java
例子中获取shp文件的几何类型,根据几何类型创建对应的样式;
也可以获取sld文件中的样式,或者通过样式对话框设置样式;
featureTypeStyle根据要素类型创建样式;
rule使用filter过滤要素和属性;
symbolizers 是一系列绘制指令

Symbology Encoding图形符号编码:
FeatureTypeStyle要素类型样式:
符号编码规范为我们提供了FeatureTypeStyle,它专注于如何以类似于CSS的方式绘制要素。
符号学编码的关键概念是:
FeatureTypeStyle:捕获绘制特定类型要素的配方
Rule规则:用于选择绘图的要素,使用符号列表来控制实际绘图过程。
Symbolizer符号:定义如何使用填充、笔画、标记和字体信息来描绘选定的要素。
在上面的Rule示例中,你可以看到这些数据结构是可变的:
Rule.isElseFilter ()
Rule.setElseFilter ()
现在提供了getter和setter。
Rule.symbolizers()提供了对List<Symbolizer>的直接访问。
你可以直接修改符号列表:
rule.clear ();rule.symbolizers()。add(pointSymbolizer);
Symbolizer符号:
Symbolizer定义几何图形如何以像素呈现;从要素中选择几何形状,并使用这里提供的信息进行绘图。
符号编码标准尽其所能在所有情况下呈现一些东西;因此,将PointSymbolizer应用于多边形将在中心绘制一个点,更有趣的是,将linessymbolizer应用于点将在指定位置绘制一条小线(固定大小)。
GeoTools扩展了由标准提供的Symbolizer概念,允许使用通用表达式(而不仅仅是PropertyName引用)定义几何图形。
此功能允许使用Function表达式定义几何形状,从而使用户有机会对几何形状进行预处理。
可用的符号有:
TextSymbolizer
用于控制贴标系统;标签是由TextSymbolizers生成的,并被扔进渲染引擎,它检测重叠,根据你定义的优先级排序,并决定最终的标签位置。
LineSymbolizer
用于控制线(或边)的绘制方式。
PolygonSymbolizer
用于控制实体形状的绘制方式。
PointSymbolizer
用于绘制点位置,实际绘制的图形被称为标记,可以选择使用一些众所周知的标记(圆形,方形等)或您自己的外部图形,如PNG图标。
RasterSymbolizer
用于控制栅格数据的渲染与完整的“彩色地图”控件。
下面是一个快速创建PointSymbolizer的例子:
//
        org.geotools.api.style.StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        FilterFactory ff = CommonFactoryFinder.getFilterFactory();

        //
        // create the graphical mark used to represent a city
        Stroke stroke = sf.stroke(ff.literal("#000000"), null, null, null, null, null, null);
        Fill fill = sf.fill(null, ff.literal(Color.BLUE), ff.literal(1.0));

        // OnLineResource implemented by gt-metadata - so no factory!
        OnLineResourceImpl svg = new OnLineResourceImpl(new URI("file:city.svg"));
        svg.freeze(); // freeze to prevent modification at runtime

        OnLineResourceImpl png = new OnLineResourceImpl(new URI("file:city.png"));
        png.freeze(); // freeze to prevent modification at runtime

        //
        // List of symbols is considered in order with the rendering engine choosing
        // the first one it can handle. Allowing for svg, png, mark order
        List<GraphicalSymbol> symbols = new ArrayList<>();
        symbols.add(sf.externalGraphic(svg, "svg", null)); // svg preferred
        symbols.add(sf.externalGraphic(png, "png", null)); // png preferred
        symbols.add(sf.mark(ff.literal("circle"), fill, stroke)); // simple circle backup plan

        Expression opacity = null; // use default
        Expression size = ff.literal(10);
        Expression rotation = null; // use default
        AnchorPoint anchor = null; // use default
        Displacement displacement = null; // use default

        // define a point symbolizer of a small circle
        Graphic circle = sf.graphic(symbols, opacity, size, rotation, anchor, displacement);
        PointSymbolizer pointSymbolizer =
                sf.pointSymbolizer("point", ff.property("the_geom"), null, null, circle);
TextSymbolizer:
GeoTools扩展了TextSymbolizer的概念,允许:
TextSymbolizer.getPriority ()
优先级用于在呈现过程中发生标签冲突时确定优先级。具有最高优先级的标签“获胜”,其他标签将被移开(在容忍范围内)或不显示。
TextSymbolizer.getOption(字符串)
用于控制呈现过程的其他特定于供应商的选项。
TextSymbolizer2.getGraphic ()
显示在文本标签后面的图形
TextSymbolizer2.getSnippet ()
文本呈现器(如KML和RSS)用于指定的片段
TextSymbolizer2.getFeatureDescription ()
由KML或RSS等格式使用,以提供有关某个特性的信息。
Fill:
填充既可用于填充多边形,也可用于创建对标记外观的更大控制(可用于定义标记的内部)。
Stroke描边:
以类似的方式,描边用于渲染边缘(多边形边缘,线条或标记的外部边缘)。
Graphic图形:
绘图时,图形的概念在许多上下文中都有使用:
图形:在渲染点位置时作为“图标”
GraphicFilter:当填充一个区域时作为一个模式
GraphicStroke:沿直线绘制时的图案
GraphicLegend:作为图例中的一个条目(GeoTools还不使用这个)
ExternalGraphic:
除了允许使用SVG和图像格式之外,还可以使用Java Icon形式的内联内容,使您能够更好地控制。您也有机会应用颜色替换(可用于根据需要将黑白图像渲染成一组颜色)。
Mark:
检查javadocs以获得众所周知的名称列表(例如“circle”或“square”)。我们感兴趣的是ExternalMark的使用,其中标记索引可用于引用真实字体中的特定字符条目。
StyleVisitor:
允许您遍历数据结构风格。
StyleFactory:
用于符号编码的对象是使用StyleFactory创建的:
org.geotools.api.style.StyleFactory sf = CommonFactoryFinder.getStyleFactory(null);
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
Fill fill = sf.fill(null, ff.literal(Color.BLUE), ff.literal(1.0));

Factory:
每个插件jar都有:
meta - inf / services文件夹
该文件夹包含一个文件列表(每个接口名称一个)
这些文件包含实现该接口的类列表

GeoTools使用Factory和FactoryRegistry类进行扩展。标准工厂模式为我们提供了即将发生的事情的线索:
工厂是一个创建其他对象的对象。
工厂以它们负责的接口命名
许多工厂只有一个单一的创建方法(这被称为工厂方法)。我们在GeoTools中有几个这样的例子,包括DataStoreFactorySpi
有些工厂有几个create方法,允许一起创建一组兼容的对象。我们有几个抽象工厂的例子,比如FeatureTypeFactory。
服务以JAR包的方式提供,查看服务实现类:
JAR包的META-INF/services目录下,文件名代表服务接口,文件内容代表服务实现类;
META-INF/services/javax.sound.sampled.spi.AudioFileReader
# Providers of sound file-reading services 
    com.acme.AcmeAudioFileReader

 META-INF/services/javax.sound.sampled.spi.AudioFileWriter
 # Providers of sound file-writing services 
    com.acme.AcmeAudioFileWriter
工厂方法允许类将实例化延迟到子类。
将对象的创建和使用分离开来,使得客户端不需要知道具体的实现细节

Function:

process:

JAI-EXT:
初始化:
static {
        JAIExt.initJAIEXT();
}
这将在JAI OperationRegistry中注册所有JAI- ext操作,以便使用它们而不是旧的JAI操作。
以上更改可以单独恢复,恢复成使用JAI操作:
JAIExt.registerJAIDescriptor("Warp") --> Replace the JAI-EXT "Warp" operation with the JAI one
JAIExt.registerJAIEXTDescriptor("Warp") --> Replace the JAI "Warp" operation with the JAI-EXT one
OperationDescriptor是一个类,描述为执行JAI/JAI- ext操作而设置的参数。
为了避免在替换与JAI/JAI- ext操作关联的OperationDescriptor后出现异常,用户应该注意如何启动操作:
1、使用ParameterBlock实例,它不提供ParameterBlockJAI类中存在的相同检查,这可能导致意外异常。这里有一个例子
2、如果存在,调用相关的GeoTools ImageWorker方法:

ContentDataStore:
JDBCDataStore、ShapefileDataStore和WFSDataStore都已经迁移到基于ContentDataStore的ng(下一代)实现上
ContentDataStore需要我们实现以下两个方法:
createTypeNames()
createFeatureSource(ContentEntry entry)
类ContentEntry有点像一个便签簿,用于跟踪每种类型的内容。

DataStore是一个表示文件、服务或数据库的对象。同时,FeatureSource表示已发布的内容、数据产品或表。
DataStore接口为客户端代码访问特性内容提供了广泛的功能。
在实现层,我们提供了getReaderInternal的单一实现。这个方法被超类ContentFeatureSource用来访问我们的内容。
您需要使用任何可用的技巧或技巧来实现抽象方法getCountInternal(Query),以返回可用特性的计数。如果没有快速生成此信息的方法,则返回-1表示查询必须逐个特性进行处理。
对于CSV文件,我们可以检查Query是否包含了所有的特性——在这种情况下,我们可以跳过头,快速计算文件中的行数。这比一次读取和解析每个特性要快得多。
featereader类似于Java Iterator结构,只是增加了FeatureType(和ioexception)。
类ContentState可用于存储所需的任何状态。开箱即用的ContentState提供了FeatureType, count和bounds的缓存。我们鼓励您创建自己的ContentState子类来跟踪其他状态,例如安全凭据或数据库连接。
FeatureReader interface:

FeatureReader.getFeatureType()

FeatureReader.next()

FeatureReader.hasNext()

FeatureReader.close()
为了实现我们的featereader,我们需要做几件事:打开一个文件,逐行通读,边读边解析特性。因为这个类实际上做了一些工作,所以我们将在代码中包含更多的注释,以保持我们的头脑清醒。
关键API联系人是为系统中的每个特性构造唯一的FeatureID。我们的惯例是在任何本地标识符(在本例中是行号)之前加上typeName前缀。
DataStoreFactorySpi提供有关构造所需参数的信息。DataStoreFactoryFinder提供了创建表示现有信息的数据存储的能力,以及创建新的物理存储的能力。
我们有两个方法来描述数据存储。
这个isAvailable方法很有趣,因为如果没有有效地实现,它可能会成为性能瓶颈。当用户尝试连接时,所有DataStoreFactorySPI工厂都将被调用,只有标记为可用的工厂才会被列入进一步交互的候选名单。


Using FeatureSource:
https://docs.geotools.org/latest/userguide/tutorial/datastore/read.html
DataStore:
CSVDataStore API for data access:

DataStore.getTypeNames()//getTypeNames方法提供了可用类型的列表。

DataStore.getSchema(typeName)//方法getSchema(typeName)提供了对由类型名引用的FeatureType的访问。

DataStore.getFeatureReader(featureType, filter, transaction)//getfeatureereader (query, transaction)方法允许访问数据存储的内容。

DataStore.getFeatureSource(typeName)//返回的实例表示数据存储提供的单个命名的FeatureType的内容。返回实例的类型指示可用的功能。
DataStore API提供了三类使内容可写的公共方法:
DataStore.createSchema(featureType) - sets up a new entry for content of the provided type

DataStore.getFeatureWriter(typeName) - a low-level iterator that allows writing

DataStore.getFeatureSource(typeName) - read-only FeatureSource or FeatureStore for read-write

FeatureSource:
FeatureSource提供了查询数据存储的能力,并表示单个featutype的内容。在我们的示例中,PropertiesDataStore表示一个充满属性文件的目录。FeatureSource将表示这些文件中的一个。
FeatureSource defines:

FeatureSource.getFeatures(query) - request features specified by query

FeatureSource.getFeatures(filter) - request features based on constraints

FeatureSource.getFeatures() - request all features

FeatureSource.getSchema() - acquire FeatureType

FeatureSource.getBounds() - return the bounding box of all features

FeatureSource.getBounds(query) - request bounding box of specified features

FeatureSource.getCount(query) - request number of features specified by query

FeatureCollection:
FeatureCollection defines:

FeatureCollection.getSchmea()

FeatureCollection.features() - access to a FeatureIterator

FeatureCollection.accepts(visitor, progress)

FeatureCollection.getBounds() - bounding box of features要素边界盒

FeatureCollection.getCount() - number of features

DataUtilities.collection(featureCollection) - used to load features into memory
FeatureResults是FeatureCollection的原始名称;
FeatureSource.count(Query.ALL)时,请注意数据存储实现可能会返回-1,表明该值对于数据存储来说太昂贵而无法计算。

CSVFeatureStore:
CSVFeatureWriter:


Library库:
api:
https://docs.geotools.org/latest/userguide/library/api/index.html
gt-api包含很多接口:
interfaces implemented by gt-main such as Feature, FeatureType, Filter and Function
interfaces implemented by gt-coverage such as GridCoverage
interfaces implemented by gt-referencing such as CoordinateReferenceSystem
interfaces implemented by gt-metadata such as Citation
Maven:
<dependency>
  <groupId>org.geotools</groupId>
  <artifactId>gt-api</artifactId>
  <version>${geotools.version}</version>
</dependency>
Contents:
Model,分为dataModel如Feature、queryModel如Filte,expression、metadataModel如FeatureType
FeatureType,提供描述所表示信息的元数据模型。这被认为是“元数据”,因为它是对存储在特性中的信息的描述。
PropertyType,类型由PropertyType, AttributeType, GeometryType, ComplexType, FeatureType表示。
Feature,用于表示“可以在地图上绘制的东西”。SimpleFeature和SimpleFeatureType简化的类
Filter,过滤器API定义了查询的第一步,它在从数据存储或目录请求数据时使用。多种方式创建Filter,判断要素是否被Filter选中。过滤器数据模型的核心是属性比较;这些过滤器允许你测试你的特性的属性,并只选择那些匹配的特性
GridCoverage,GridCoverage是一个数值矩阵,其中包含有关数值含义和数值地理位置的信息。
Coordinate Systems,坐标系统
Style Layer Descriptor,styelayerdescriptor的概念来自于SLD规范。它旨在定义web地图服务器如何绘制整个地图(将所有图层包含在一起)。
Symbology Encoding, 图形符号编码
Progress,回调对象ProgressListener用于报告长时间运行的操作,并为最终用户提供取消这些操作的机会。
Name and Record
Text,字符串
Parameter,参数
Unit,单位
Range,数值和数值范围处理
URLs,处理URL
Utilities,
JTS:
它使用像坐标点多边形和线串这样的结构来捕获形状
请记住,JTS是纯拓扑,几何对象是没有意义的纯形状。
有时你需要生成一条平滑的曲线,保证它通过一组指定的点。最可靠的方法是使用样条函数。这将生成一组多项式(三次)曲线,每条曲线都适合数据的一部分,并平滑地连接到相邻的曲线。
经过点的平滑曲线:
Splines:

public Geometry splineInterpolatePoints(double[] xPoints, double[] yPoints) {
   /*
   * First we create a LineString of segments with the
   * input points as vertices.
   */
   final int N = xPoints.length;
   Coordinate[] coords = new Coordinate[N];
   for (int i = 0; i < N; i++) {
      coords[i] = new Coordinate(xPoints[i], yPoints[i]);
   }
   GeometryFactory gf = new GeometryFactory();
   LineString line = gf.createLineString(coords);

   /*
   * Now we use the GeoTools JTS utility class to smooth the
   * line. The returned Geometry will have all of the vertices
   * of the input line plus extra vertices tracing a spline
   * curve. The second argument is the 'fit' parameter which
   * can be in the range 0 (loose fit) to 1 (tightest fit).
   */
   return JTS.smooth(line, 0.0);
}
Example smoothing a polygon平滑多边形例子:

WKTReader reader = new WKTReader();
Geometry tShape = reader.read(
"POLYGON((10 0, 10 20, 0 20, 0 30, 30 30, 30 20, 20 20, 20 0, 10 0))");
Geometry tLoose = JTS.smooth(tShape, 0.0);
Geometry tTighter = JTS.smooth(tShape, 0.75);

Geometry:
getArea() - area returned in the same units as the coordinates (be careful of latitude/longitude data!)
getCentroid() - the center of the geometry
getEnvelope() - returns a geometry which is probably not what you wanted
getEnvelopeInternal() - this returns a useful Envelope
getInteriorPoint() - the center of the geometry (that is actually on the geometry)
getDimension()
几何关系由以下返回真或假的函数表示:
disjoint(Geometry) - same as “not” intersects不相交
touches(Geometry) - geometry have to just touch, crossing or overlap will not work几何必须只是接触,交叉或重叠将不工作
intersects(Geometry)相交
crosses(Geometry)交叉
within(Geometry) - geometry has to be full inside几何全部在之内
contains(Geometry)包含
overlaps(Geometry) - has to actually overlap the edge, being within or touching will not work实际上要重叠边缘,在里面或触摸是不行的
covers(Geometry)覆盖
coveredBy(Geometry)被覆盖
relate(Geometry, String) - allows general check of relationship see dim9 page允许关系的一般检查见dim9页
relate(Geometry)关系检查
根据两种几何形状确定形状:
intersection(Geometry)相交
union(Geometry)结合
difference(Geometry)区别
symDifference(Geometry)
一些最有用的函数是:
distance(Geometry)距离
buffer(double) - used to buffer the edge of a geometry to produce a polygon用于缓冲几何体的边缘以生成多边形
union() - used on a geometry collection to produce a single geometry用于几何集合以生成单个几何
下面是最难的三种方法(后面会详细讨论):
equals(Object) - normal Java equals which checks that the two objects are the same instance普通Java equals检查两个对象是否为同一实例
equals(Geometry) - checks if the geometry is the same shape检查几何形状是否相同
equalsExact(Geometry) - check if the data structure is the same检查数据结构是否相同
有一些簿记方法可以帮助我们发现几何图形是如何构造的:
getGeometryFactory()
getPreceisionModel()
toText() - the WKT representation of the Geometry几何的WKT表示
getGeoemtryType() - factory method called (i.e. point, linestring, etc..)工厂方法调用(即点,linestring等…)
有几种方法可以存储开发者信息:
getSRID() - stores the “spatial reference id”, used as an external key when working with databases存储“空间引用id”,在使用数据库时用作外部键
getUserData() - 为了供开发人员使用,最佳实践是存储java.util.Map。GeoTools偶尔会使用这个字段来存储srsName或完整的CoordinateReferenceSystem。

Geometries Enumeration几何枚举:
使用Geometry的代码最终会进行大量的instanceof测试,以确定正在使用哪种Geometry(以采取适当的操作)。
我们定义了一个枚举来帮助解决这个问题:
public boolean hit(Point point, Geometry geometry) {
        final double MAX_DISTANCE = 0.001;

        switch (Geometries.get(geometry)) {
            case POINT:
            case MULTIPOINT:
            case LINESTRING:
            case MULTILINESTRING:
                // Test if p is within a threshold distance
                return geometry.isWithinDistance(point, MAX_DISTANCE);

            case POLYGON:
            case MULTIPOLYGON:
                // Test if the polygonal geometry contains p
                return geometry.contains(point);

            default:
                // For simplicity we just assume distance check will work for other
                // types (e.g. GeometryCollection) in this example
                return geometry.isWithinDistance(point, MAX_DISTANCE);
        }
    }
PrecisionModel精确模型:
开箱即用的JTS使用默认的双精度模型。用PrecisionModel配置你的GeometryFactory允许你在不同于默认的分辨率下工作。
PrecisionModel是处理几何问题时“数值稳定性”的核心。当处理较大的值时,Java内建的数学不是很精确。通过在PrecisionModel中显式地捕获“舍入”过程,JTS允许管理这些类型的错误,并为您的工作在速度和准确性之间做出适当的权衡。
基于小数位定义模型:
  double scale = Math.pow(10, numDecPlaces);
  PrecisionModel pm = new PrecisionModel(scale);
  GeometryFactory gf = new GeometryFactory(pm);

  Geometry testPoint = gf.createPoint(new Coordinate(x, y));
  return poly.contains(testPoint);
}
通过常量定义模型:
pm = new PrecisionModel( PrecisionModel.Type.FIXED ); // fixed decimal point
pm = new PrecisionModel( PrecisionModel.Type.FLOATING ); // for Java double
pm = new PrecisionModel( PrecisionModel.Type.FLOATING_SINGLE ); // for Java float

Envelope边界:
JTS拓扑边界具有按x1,x2, y1,y2顺序记录边界的概念。
double xMin = envelope.getMinX();
double yMin = envelope.getMinY();

double xMax = envelope.getMaxX();
double yMax = envelope.getMaxY();

double width = envelope.getWidth(); // assuming axis 0 is easting
double height = envelope.getHeight(); // assuming axis 1 is nothing

// Expand an existing envelope
Envelope bbox = new Envelope();
envelope.expandToInclude(bbox);

// Use
envelope.covers(5, 10); // inside or on edge!
envelope.contains(5, 10); // inside only

// Null
envelope.isNull(); // check if "null" (not storing anything)
envelope.setToNull();

Envelope Transform边界变换:
使用JTS Utility类转换边界:
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:23032");

Envelope envelope = new Envelope(0, 10, 0, 20);

MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS);

Envelope quick = JTS.transform(envelope, transform);

// Sample 10 points around the envelope在边界周围抽取10个点
Envelope better = JTS.transform(envelope, null, transform, 10);

ReferencedEnvelope:
GeoTools ReferencedEnvelope扩展了JTS Envelope来实现gwt -api模块的Bounds接口。
ReferencedEnvelope是所有这些:
org.locationtech.jts.geom.Envelope——由JTS拓扑系统定义(SQL概念的一个简单特性)
org.locationtech.jts.geom.Envelope -由ISO 19107 Geometry定义的2D边界
org.geotools.api.geometry.Bounds -捕获由ISO 19107 Geometry定义的3D边界。
为了支持3D边界(并使用3D坐标参考系统),我们必须创建子类referencedenvele3d的实例(见下文)。
使用ReferencedEnvelope是GeoTools中边界的最常见表示。构造函数期望范围按照xMin,xMax,yMin,yMax的顺序定义。
ReferencedEnvelope envelope =
        new ReferencedEnvelope(0, 10, 0, 20, DefaultGeographicCRS.WGS84);

double xMin = envelope.getMinX();
double yMin = envelope.getMinY();

double xMax = envelope.getMaxX();
double yMax = envelope.getMaxY();

double width = envelope.getWidth();
double height = envelope.getHeight();

double xCenter = envelope.getMedian(0);
double yCenter = envelope.getMedian(1);

CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
int dimension = envelope.getDimension();

// Direct access to internal upper and lower positions
Position lower = envelope.getLowerCorner();
Position upper = envelope.getUpperCorner();

// expand to include 15, 30
envelope.include(15, 30);

envelope.isEmpty(); // check if storing width and height are 0检查宽高是否为0

envelope.isNull(); // check if "null" (not storing anything)检查是否为null
envelope.setToNull();

ReferencedEnvelope Transform:
ReferencedEnvelope有一件事做得很好;它是一个JTS边界,有一个坐标参考系统。使用这个coordinatereference系统,你可以在投影之间快速转换。
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
ReferencedEnvelope envelope = new ReferencedEnvelope(0, 10, 0, 20, sourceCRS);

// Transform using 10 sample points around the envelope在包络线周围使用10个采样点进行变换
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:23032");
ReferencedEnvelope result = envelope.transform(targetCRS, true, 10);
ReferencedEnvelope在许多GeoTools接口中使用,以表示对拥有一个coordinatereferencesystem的需求。
使用没有ReferencedEnvelope的FeatureSource示例:
Envelope bounds = featureSource.getBounds();
CoordinateReferenceSystem crs = featureSource.getSchema().getDefaultGeometry().getCoordinateSystem();
在ReferencedEnvelope中使用FeatureSource:
ReferencedEnvelope bounds = (ReferencedEnvelope) featureSource.getBounds();
CoordinateReferenceSystem crs = bounds.getCoordinateReferenceSystem();

ReferencedEnvelope3D:
GeoTools referencedenvele3d扩展了JTS Envelope来实现gt-api模块Bounds3D接口。
referencedenvele3d是所有这些:
ReferencedEnvelope包括所有父类和接口
org.geotools.api.geometry.BoundingBox3D -由ISO 19107几何定义的3D边界
当你想在GeoTools中表示3D边界时,这个类就会用到。构造函数期望以xMin,xMax,yMin,yMax,zMin,zMax的顺序输入,并期望一个3D CRS:
ReferencedEnvelope3D envelope =
        new ReferencedEnvelope3D(0, 10, 0, 20, 0, 30, DefaultGeographicCRS.WGS84_3D);

double xMin = envelope.getMinX();
double yMin = envelope.getMinY();
double zMin = envelope.getMinZ();

double xMax = envelope.getMaxX();
double yMax = envelope.getMaxY();
double zMax = envelope.getMaxZ();

double width = envelope.getWidth();
double height = envelope.getHeight();
double depth = envelope.getDepth();

double xCenter = envelope.getMedian(0);
double yCenter = envelope.getMedian(1);
double zCenter = envelope.getMedian(2);

CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
int dimension = envelope.getDimension();

// Direct access to internal upper and lower positions直接进入内部上下级职位
Position lower = envelope.getLowerCorner();
Position upper = envelope.getUpperCorner();

// expand to include 15, 30, 40扩展到包括15,30,40
envelope.include(15, 30, 40);

envelope.isEmpty(); // check if storing width and height are 0检查存储宽度和高度是否为0

envelope.isNull(); // check if "null" (not storing anything)检查是否为“null”(不存储任何内容)
envelope.setToNull();

ReferencedEnvelope utility methods参考边界工具方法:
当使用3D CoordinateReferenceSystem时,我们必须创建一个referencedenvele3d的实例,而不是其父类ReferencedEnvelope的实例。
如果我们不确定我们正在处理的维度,有安全的方法来创建,复制,转换或引用ReferencedEnvelope实例:
create()方法:安全地创建一个新的ReferencedEnvelope实例(总是创建一个副本)
Rect()方法:安全地从java创建awt矩形
envelope()方法:安全地创建一个jts信封
reference()方法:安全地将现有对象“强制转换”给ReferencedEnvelope(只在需要时复制)
ReferencedEnvelope工具程序方法的示例使用:
// can hold both regular ReferencedEnvelope as well as ReferencedEnvelope3D可以容纳常规的ReferencedEnvelope以及referencedenvele3d
ReferencedEnvelope env;
// can be instance of ReferencedEnvelope3D;可以是referencedenvele3d的实例;
ReferencedEnvelope original = null;
// can be 2D or 3D是2D还是3D
CoordinateReferenceSystem crs = null;
// can be instance of ReferencedEnvelope(3D)ReferencedEnvelope(3D)的一个实例
Bounds opengis_env = null;
// can be instance of ReferencedEnvelope(3D)ReferencedEnvelope(3D)的一个实例
org.locationtech.jts.geom.Envelope jts_env = null;
// can be instance of ReferencedEnvelope or ReferencedEnvelope3D可以是ReferencedEnvelope或referencedenvele3d的实例
BoundingBox bbox = null;

// safely copy ReferencedEnvelope, uses type of original to determine type安全复制ReferencedEnvelope,使用原始的类型来确定类型
env = ReferencedEnvelope.create(original);

// safely create ReferencedEnvelope from CRS, uses dimension to determine type安全地从CRS创建ReferencedEnvelope,使用尺寸来确定类型
env = ReferencedEnvelope.create(crs);

// safely create ReferencedEnvelope from org.geotools.api.geometry.Envelope,
// uses dimension in Envelope to determine type从org.geotools.api. geometric . envelope创建ReferencedEnvelope,使用信封的尺寸来确定类型
env = ReferencedEnvelope.create(opengis_env, crs);

// safely create ReferencedEnvelope from org.locationtech.jts.geom.Envelope,
// uses dimension in Envelope to determine type从org. locationtechnical .jts. geom.envelope安全地创建ReferencedEnvelope,使用信封的尺寸来确定类型
env = ReferencedEnvelope.envelope(jts_env, crs);

// safely reference org.geotools.api.geometry.Envelope as ReferencedEnvelope
// --> if it is a ReferencedEnvelope(3D), simply cast it; if not, create a conversion安全引用org.geotools.api. geometric . envelope作为ReferencedEnvelope,-->如果它是一个ReferencedEnvelope(3D),简单的cast它;如果没有,创建一个转换
env = ReferencedEnvelope.reference(opengis_env);

// safely reference org.locationtech.jts.geom.Envelope as ReferencedEnvelope
// --> if it is a ReferencedEnvelope(3D), simply cast it; if not, create a conversion安全地引用org. locationtechnologies .jts. geom.envelope作为ReferencedEnvelope,-->如果它是一个ReferencedEnvelope(3D),简单的cast它;如果没有,创建一个转换
env = ReferencedEnvelope.reference(jts_env);

// safely reference BoundingBox as ReferencedEnvelope
// --> if it is a ReferencedEnvelope(3D), simply cast it; if not, create a conversion安全地引用BoundingBox作为ReferencedEnvelope, -->如果它是一个ReferencedEnvelope(3D),简单的cast它;如果没有,创建一个转换
env = ReferencedEnvelope.reference(bbox);

JTS Utility ClassJTS工具类:
JTS Utility类用于平滑一些常见的JTS Geometry活动。
Distance:
有一个辅助方法允许你计算两点之间的实际距离:
double distance = JTS.orthodromicDistance(start, end, crs);
        int totalmeters = (int) distance;
        int km = totalmeters / 1000;
        int meters = totalmeters - (km * 1000);
        float remaining_cm = (float) (distance - totalmeters) * 10000;
        remaining_cm = Math.round(remaining_cm);
        float cm = remaining_cm / 100;

        System.out.println("Distance = " + km + "km " + meters + "m " + cm + "cm");
在内部,这个方法使用了GeodeticCalculator,它提供了一个更通用的解决方案,可以取任意两点之间的距离(即使它们是在不同的坐标参考系中提供的)。
Transform:
您可以直接使用MathTransform——它有一些方法可以一次提供一个Position实例、转换并返回修改后的Position。
挑战在于我们的JTS Geometry实例是基于Coordinate实例而不是Position实例构建的。
JTS实用程序类为这个常见的活动定义了一个助手方法:
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;

CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:23032");

MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS);
Geometry targetGeometry = JTS.transform( sourceGeometry, transform);
作为一个快速的例子,你可以利用仿射变换来执行简单的变换,如旋转和缩放:
Coordinate ancorPoint = geometry.getCentroid(); // or some other point
AffineTransform affineTransform = AffineTransform.getRotateInstance(angleRad, ancorPoint.x, ancorPoint.y);
MathTransform mathTransform = new AffineTransform2D(affineTransform);

Geometry rotatedPoint = JTS.transform(geometry, mathTransform);
同样的方法也适用于JTS坐标:
// by default it can make a new Coordinate for the result
Coordinate targetCoordinate = JTS.transform( coordinate, null, transform );

// or make use of an existing destination coordinate (to save memory)
JTS.transform( coordinate, destination, transform );

// or modify a coordinate in place
JTS.transform( coordinate, coordinate, transform );
还有一个JTS包络,虽然这种情况有点特殊,因为你有机会指定沿边界的多少个点被采样。如果您指定5,则沿上、下、左、右边缘的5个点将被转换-使您有机会更好地解释地球的曲率:
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
        CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:23032");

        Envelope envelope = new Envelope(0, 10, 0, 20);

        MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS);

        Envelope quick = JTS.transform(envelope, transform);

        // Sample 10 points around the envelope
        Envelope better = JTS.transform(envelope, null, transform, 10);
最后是DefaultGeographicCRS的共同目标。WGS84给出了自己的方法(快速转换到地理边界):
Envelope geographicBounds = JTS.toGeographic( envelope, dataCRS );
最后,有一个非常快速的方法可以直接对双精度数组执行转换:
JTS.xform( transform, sourceArray, destinationArray );

Convert:
有许多方法可以帮助将JTS Geometry转换为引用模块使用的一些ISO Geometry思想。
快速从JTS包络转换为ISO几何包络(使用提供的coordinatereferencessystem):
Envelope envelope = geometry.getEnvelopeInternal();

// create with supplied crs
GeneralBounds bounds = JTS.getGeneralBounds( envelope, crs );

// Check geometry.getUserData() for srsName or CoordinateReferenceSystem
ReferencedEnvelope bounds = JTS.toEnvelope( geometry );
这里也有大量的方法来帮助你从各种来源创建一个几何图形:
// Convert a normal JTS Envelope
Polygon polygon = JTS.toGeometry( envelope );

// The methods take an optional GeometryFactory
Polygon polygon2 = JTS.toGeometry( envelope, geometryFactory );

// Or from ISO Geometry BoundingBox (such as ReferencedEnvelope)
Polygon polygon3 = JTS.toGeometry( bounds );

// Or from a Java2D Shape
Geometry geometry = JTS.toGeometry( shape );

Smooth:
最近增加的是使用样条将一个几何图形“平滑”成一条包含所有点的曲线。
// The amount of smoothing can be set between 0.0 and 1.0
Geometry geometry = JTS.smooth( geometry, 0.4 );

Testing equality of Geometry objects几何对象的相等性测试:
JTS有许多不同的equals方法来比较几何对象。如果您要对大型、复杂的几何对象进行大量比较,那么了解这些方法之间的差异对于在应用程序中获得最佳的运行时性能非常重要。
如果这个页面看起来又长又吓人,重要的一点是要避免使用Geometry.equals(Geometry g),用equalsExact或equalsTopo 代替;
Geometry.equalsExact( Geometry g ):
此方法测试几何对象的结构相等性。简单地说,这意味着它们必须具有相同数量的顶点、相同的位置和相同的顺序。后一种情况比较棘手。如果两个多边形有匹配的顶点,但是一个是顺时针排列的,而另一个是逆时针排列的,那么这个方法将返回false。了解这一点很重要,因为当对象被存储在数据存储中以及之后从数据存储中检索时,顶点顺序可能会发生变化。
Geometry.equalsExact( Geometry g, double tolerance ):
这与前一种方法类似,但允许您指定顶点坐标比较的容差。
Geometry.equalsNorm( Geometry g ):
这种方法通过在比较之前将几何对象规范化(即将每个对象放入标准或规范形式),使您摆脱了上面提到的顶点顺序问题。它相当于
geomA.normalize();
geomB.normalize();
boolean result = geomA.equalsExact( geomB );
顶点顺序将保证是相同的,但代价是额外的计算,对于复杂的几何对象,这可能是昂贵的。
Geometry.equalsTopo( Geometry g ):
此方法测试拓扑相等性,这相当于绘制两个几何对象并查看它们的所有组成边是否重叠。这是一种最稳健的比较,但也是计算成本最高的比较。
Geometry.equals( Object o ):
这个方法是Geometry的同义词。并允许您在Java集合中使用几何对象。
Geometry.equals( Geometry g ):
这个方法是Geometry.equalsTopo的同义词。它实际上应该附带一个健康警告,因为它的存在意味着当您只需要快速便宜的比较时,您可能会在不知不觉中进行计算上昂贵的比较。例如:
Geometry geomA = ...
Geometry geomB = ...

// If geomA and geomB are complex, this will be slow:
boolean result = geomA.equals( geomB );

// If you know that a structural comparison is all you need, do
// this instead:
result = geomA.equalsExact( geomB );
使用这种方法最好的办法就是发誓永远不要使用它。

Geometry Relationships几何拓扑关系:

将几何图形表示为对象的一个主要目的是能够建立关系
你可以使用JTS来检查两个对象是否相等:

WKTReader reader = new WKTReader( geometryFactory );
LineString geometry = (LineString) reader.read("LINESTRING(0 0, 2 0, 5 0)");
LineString geometry = (LineString) reader.read("LINESTRING(5 0, 0 0)");

return geometry.equals( (Geometry) geometry2 );
请注意,equals并不是您所期望的快速简单的检查,而是对数据结构含义的真正完整的空间比较。上面的代码示例将返回true,因为这两行字符串定义的形状完全相同。
强迫你自己学会把(Geometry) 强转放在那里的习惯;只有习惯才能避免与对象等号混淆。
如果加了(Geometry) 强转就是空间对象比较,如果不加就是对象的比较。
如果在无效的几何图形上调用此方法将失败。
常见错误-与对象等号混淆
下面是这个常见错误的一个例子:
return geometry.equals( other ); // will use Object.equals( obj ) which is the same as the == operator
以下是更正:
return geometry.equals( (Geometry) other );
Equals Exact Relationship等于精确关系:
你可以检查两个几何图形是否完全相等;一直到坐标级别:
return geometry.equalsExact( (Geometry) geometry2 );
https://docs.geotools.org/latest/userguide/library/jts/relate.html
此方法比equals(geometry)更快,并且更接近于普通Java程序对数据对象equals方法实现的假设。我们正在检查内部结构;而不是意思。
equalsExact方法能够对无效的几何图形起作用。
替代-身份运算符
普通Java标识操作符也有它的位置;别忘了这一点:
return geometry == geometry2;

Disjoint不相交:
这些几何图形没有共同点。
return geometry.disjoint( geometry2 );

Intersects相交:
这些几何图形至少有一个共同点。
return geometry.intersects( geometry2 );
这将测试边界上或几何体内的任何点是否为边界的一部分或在第二个几何体内。
这是disjoint的反义词:
return !geometryA.disjoint( geometry2 );

Touches接触:
几何图形只接触边缘,不以任何方式重叠:
return geometryA.touches( geometry2 );

Crosses交叉:
这些几何图形不仅仅是相互接触,它们实际上是重叠边界的:
return geometryA.crosses( geometry2 );

Within在内部:
一个几何体完全在另一个几何体内(没有接触边缘):
return geometryA.within( geometry2 );

Contains包含:
一个几何包含另一个几何:
return geometryA.contains( geometry2 );

Overlaps重叠:
这些几何图形有一些共同点;但并非所有点都是共同的(所以如果一个几何图形在另一个几何图形内部重叠将是错误的)。重叠部分必须与两个几何形状相同;因此,两个多边形如果在一点上接触,就不被认为是重叠的。
return geometryA.overlaps( geometry2 );
重叠关系的定义与普通英语中使用的有一点不同(通常你会假设一个几何包含在另一个几何中是“重叠的”;使用十字路口来测试这种情况)

Relates关系:
计算“DE-9IM矩阵”为两个几何形状,让你研究他们是如何相互作用的。
IntersectionMatrix m = a.relate(b);
IntersectionMatrix允许你分别测试两个几何图形的内部、外部和边缘如何相互作用。以上所有操作都可以看作是这个IntersectionMatrix的总结。

点集理论与DE-9IM矩阵:
两个几何的交点:
区域之间的关系被描述为通过比较两个区域的内部、边界和外部属性的交集而产生的矩阵。这种比较被称为维度扩展的9相交矩阵或DE-9IM。
9-交集矩阵列出了每个几何图形的内部、边界和外部与其他几何图形的交集(总共有9种组合)。

Geometry Operations几何操作:
JTS几何操作用于执行一系列空间计算;从求交点到确定质心。
Buffer缓冲
Intersection相交
ConvexHull凸包
Union结合
Difference差集
SymDifference
JTS Geometry操作严格遵循SQL规范的简单特性;因此,如果您对究竟发生了什么有任何疑问,请查看相关规范。

Buffer:
创建包含设定距离内所有点的多边形或多多边形:
Geometry buffer = geometry.buffer( 2.0 ); // note distance is in same units as geometry距离和几何的单位一致
请记住,缓冲区是使用与您的坐标相同的距离单位定义的,并且仅在2D中计算。您可能希望转换几何图形,对其进行缓冲,然后在使用DefaultGeographicCRS.WGS84等实际单位时将结果转换回来。

Intersection:
提供两个几何图形之间的公共形状:
Geometry intersection = polygon.intersection( line );

Closing a LineString:
这里有几个场合,你需要取一个LineString并关闭它(这样开始点和结束点是完全相同的)。这一步需要创建一个线性环,作为用户提供的多边形的外部边界。
CoordinateList list = new CoordinateList( lineString.getCoordinates() );
list.closeRing();
LinearRing ring = factory.createLinearRing( list.toCoordinateArray() );

Copy Coordinates复制坐标:
LinearRing ring = null;
if( lineString.isClosed() )
   ring = factory.createLinearRing( splitter.getCoordinateSequence() );
else {
   CoordinateSequence sequence = lineString.getCoordinateSequence();
   Coordinate array[] = new Coordinate[ sequence.size() + 1 ];
   for( int i=0; i<sequence.size();i++){
   array[i] = sequence.getCoordinate(i);
   array[array.length-1] = sequence.getCoordinate(0);
   ring = factory.createLinearRing( array );
}
Polygon polygon = factory.createPolygon( ring, null );


Combine Geometry结合几何:
对两个几何图形进行联合运算是你可能做的最昂贵的运算之一;虽然对于较小的数字(例如5-10)是合理的,但当您开始处理多达数百个几何形状时,成本可以在几分钟内测量。
使用GeometryCollection union():
static Geometry combineIntoOneGeometry( Collection<Geometry> geometryCollection ){
     GeometryFactory factory = FactoryFinder.getGeometryFactory( null );
     // note the following geometry collection may be invalid (say with overlapping polygons)注意以下几何集合可能无效(比如重叠多边形)
     GeometryCollection geometryCollection =
          (GeometryCollection) factory.buildGeometry( geometryCollection );
     return geometryCollection.union();
 }

Using buffer( 0 ):
你可以在JTS 1.8中使用buffer(0)获得相同的效果:
GeometryFactory factory = FactoryFinder.getGeometryFactory( null );
// note the following geometry collection may be invalid (say with overlapping polygons)
GeometryCollection geometryCollection =
         (GeometryCollection) factory.buildGeometry( geometryCollection );
Geometry union = geometryCollection.buffer(0);

Using union( geometry ):
使用1.9之前的JTS版本,您需要使用geometry逐个组合几何图形。联合(几何):
 static Geometry combineIntoOneGeometry( Collection<Geometry> geometryCollection ){
        Geometry all = null;
        for( Iterator<Geometry> i = geometryCollection.iterator(); i.hasNext(); ){
        Geometry geometry = i.next();
        if( geometry == null ) continue;
        if( all == null ){
            all = geometry;
        }
        else {
            all = all.union( geometry );
        }
    }
    return all;
}
上面的代码太简单了;正确的做法是将数据分成不同的区域,将一个区域内的所有几何图形合并在一起;然后在最后将这些组合成一个大的几何图形(这是上面union()方法使用的方法)。

Main Data API:
支持从一系列数据源访问特征信息(即向量信息)。还可以从gwt -jdbc获得用于数据库访问的其他数据存储插件。
DataStore API是关于将数据(通常以特性的形式)从外部服务、磁盘文件等提升到你的应用程序中。在这里,您终于可以开始使用工具包来处理真实的信息。

DataStore:
数据存储用于以各种矢量格式访问和存储地理空间数据,包括形状文件、GML文件、数据库、Web Feature Servers和其他格式。
Create创建DataStore:
不建议手工创建数据存储;相反,我们使用FactoryFinder来查找支持所请求格式的正确插件。
我们有三种工厂查找器可供选择:
DataAccessFinder用于获取DataAccess。DataAccessFinder将找出由DataStoreFinder找到的那些数据存储和那些只实现DataAccess而不实现DataStore的数据存储。
DataStoreFinder用于获取数据存储。
FileDataStoreFinder仅限于与FileDataStoreFactorySpi一起工作,其中明确的文件扩展名可用。
如果我们连接到现有的内容,或要求创建一个新的文件,我们将处理的事情有点不同。

Access:
我们现在只关注最常见的情况——访问现有的Shapefile。请放心,在与PostGIS这样的真实数据库或WFS这样的web服务对话时,您在这里学到的东西将会很好地工作。
要创建shapefile,我们将使用DataStoreFinder实用程序类。这是它的样子:
File file = new File("example.shp");
Map map = new HashMap();
map.put( "url", file.toURL() );
DataStore dataStore = DataStoreFinder.getDataStore(map );
Create:
要在磁盘上创建一个新的shapefile,我们必须再深入一步,向FileDataStoreFinder请求与shp扩展名匹配的工厂。
FileDataStoreFactorySpi factory = FileDataStoreFinder.getDataStoreFactory("shp");
File file = new File("my.shp");
Map map = Collections.singletonMap( "url", file.toURL() );
DataStore myData = factory.createNewDataStore( map );
FeatureType featureType = DataUtilities.createType( "my", "geom:Point,name:String,age:Integer,description:String" );
myData.createSchema( featureType );

Factory:
我们可以重复创建新shapefile的示例,只需使用DataStoreFinder列出可用的实现,然后查看哪一个愿意创建shapefile。
这次你需要手工做这项工作:
Map map = new HashMap();
map.put( "url", file.toURL());
for( Iterator i=DataStoreFinder.getAvailableDataStores(); i.hasNext(); ){
    DataStoreFactorySpi factory = (DataStoreFactorySpi) i.next();
    try {
        if (factory.canProcess(params)) {
            return fac.createNewDataStore(params);
        }
    }
    catch( Throwable warning ){
        System.err( factory.getDisplayName() + " failed:"+warning );
    }
}
如您所见,该逻辑仅从可以为我们创建数据存储的第一个工厂返回一个数据存储。
这些例子引出了几个问题:
问:为什么要使用DataStoreFinder
我们使用的是FactoryFinder(而不是new ShapefileDataStore),所以GeoTools可以查看您的具体配置,并为工作选择正确的实现。存储实现可能会随时间变化,通过工厂访问存储可确保在发生这种情况时不需要更改客户端代码。
问:我们在地图上放什么?
这是一个很难回答的问题,迫使我们阅读文档:
文档:形状(用户指南)
ShapefileDataStoreFactory (javadoc)
这些信息也可以通过DataStoreFactorySpi、getParameterInfo()方法在运行时获得。您可以使用此信息在动态应用程序中创建用户界面。

Catalog:
如果您正在使用GeoServer或uDig,那么在创建数据存储之前,您可以访问一些很棒的工具来定义数据存储。可以将其视为“及时”或“惰性”数据存储。
ServiceFinder finder = new DefaultServiceFactory( catalog );
File file = new File("example.shp");
Service service = finder.acquire( file.toURI() );
// Getting information about the Shapefile (BEFORE making the DataStore)
IServiceInfo info = service.getInfo( new NullProgressListener() );
String name = info.getName();
String title = info.getTitle().toString();
// Making the DataStore
DataStore dataStore = service.resolve( DataStore.class, new NullProgressListener() );

Careful:
Don’t Duplicate
数据存储表示与文件或数据库的实时连接:
不要创建和丢弃数据存储,也不要创建副本
数据存储是大的、重的对象——它们中的许多都在为您处理数据库连接或加载空间索引。
请保留您的数据存储以供重用
将它们作为Singleton进行管理
在注册表中管理它们
在特定于应用程序的Catalog中管理它们
有关更多详细信息,请访问Repository

Direct Access:
您还可以避开FactoryFinder,并使用以下快速技巧。
这是不明智的(因为实现可能会随着时间的推移而改变),但这是如何做到的。
使用ShapefileDataStore:
File file = new File("example.shp");
DataStore shapefile = new ShapefileDataStore( example.toURL());
shapefile.setNamespace(new URI("refractions"));
shapefile.setMemoryMapped(true  ;

String typeName = shapefile.getTypeName(); // should be "example"
FeatureType schema = shapefile.getSchema( typeName ); // should be "refractions.example"

FeatureSource contents = shapefile.getFeatureSource( typeName );
int count = contents.getCount( Query.ALL );
System.out.println( "Connected to "+file+ " with " + count );
对于一个快速的代码示例来说,这种方法可能很好,但是在实际应用程序中,我们可以要求您使用DataStoreFactoryFinder。它将让库找出合适的实现。
使用ShapefileDataStoreFactory:
FileDataStoreFactorySpi factory = new ShapefileDataStoreFactory();

File file = new File("example.shp");
Map map = Collections.singletonMap( "url", file.toURL() );

DataStore dataStore = factory.createDataStore( map );
这个hack有点难以避免——因为你确实想在某些情况下直接使用工厂(例如在磁盘上创建一个全新的文件时)。如果可能的话,向DataStoreFactoryFinder查询所有可用的工厂(这样您就可以利用运行时可用的工厂)。

FeatureSource:
FeatureSource可能是你来这个聚会的原因;它允许您以Java对象的形式访问地理空间信息。
如果当前用户具有修改或锁定特性的权限,则检查一个FeatureSource可能支持额外的接口FeatureStore和FeatureLocking。
SimpleFeatureSource:
首先,我们将重点关注SimpleFeatureSource,它主要用于访问要素。
Access Features访问要素:
你可以使用一个方法调用来访问所有的特性:
SimpleFeatureSource featureSource = dataStore.getFeatureSource(featureName);
SimpleFeatureCollection collection = featureSource.getFeatures();
这与要求文件或表中包含的所有功能相同:
SimpleFeatureSource featureSource = dataStore.getFeatureSource(featureName);
SimpleFeatureCollection collection = featureSource.getFeatures( Filter.INCLUDE );
使用过滤器访问要素:
Filter filter = CQL.filter("NAME == 'Hwy 31a');
SimpleFeatureCollection collection = featureSource.getFeatures( filter );
这是非常有效的,因为在处理数据库时,Filter通常可以归结为原始SQL语句。任何后端不支持的功能都作为客户端GeoTools的一部分进行处理
你可以使用Query请求一组有限的属性;还可以按特定的顺序要求内容。
FilterFactory ff = ...
Filter filter = ...
String typeName = ...
Query query = new Query(typeName, filter);
query.setMaxFeatures(10);
query.setPropertyNames(new String[]{"the_geom", "name"});

SortBy sortBy = ff.sort("name", SortOrder.ASCENDING);
query.setSortBy(new SortBy[]{sortBy});

SimpleFeatureCollection collection = featureSource.getFeatures( query );
您可以使用提示作为查询的一部分,以微调性能和功能;有关更多信息,请参阅提示javadocs。
FilterFactory ff = ...
Filter filter = ...
String typeName = ...
Query query = new Query(typeName, filter);
query.setPropertyNames(new String[]{"the_geom", "name"});

query.setHints( new Hints( Hints.FEATURE_2D, Boolean.true ); // force 2D data
SimpleFeatureCollection collection = featureSource.getFeatures( query );
快速只请求featureid(而不请求内容):
SimpleFeatureCollection featureCollection = featureSource.getFeatures( Query.FIDS );
Summary总结:
一个简单的计数是可用的:
SimpleFeatureType schema = featureSource.getSchema();
Query query = new Query( schema.getTypeName(), Filter.INCLUDE );
int count = featureSource.getCount( query );
if( count == -1 ){
  // information was not available in the header!
  SimpleFeatureCollection collection = featureSource.getFeatures( query );
  count = collection.size();
}
System.out.println("There are "+count+" "+schema.getTypeName()+ " features");
一组要素的边界或延伸:
Query query = new Query( schema.getTypeName(), Filter.INCLUDE );
BoundingBox bounds = featureSource.getBounds( query );
if( bounds == null ){
   // information was not available in the header
   FeatureCollection<SimpleFeatureType, SimpleFeature> collection = featureSource.getFeatures( query );
   bounds = collection.getBounds();
}
System.out.println("The features are contained within "+bounds );
使用要素集合的聚合函数可以获得特别的汇总信息:
SimpleFeatureCollection collection = featureSource.getFeatures();

FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
Function sum = ff.function("Collection_Sum", ff.property("population"));

Object value = sum.evaluate( featureCollection );
System.out.println("total population: "+ sum );

SimpleFeatureStore:
FeatureStore是我们最终获得将信息写入磁盘、数据库或web服务的地方。
问:如何判断您是否具有读写访问权限?
使用check的实例:
FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore.getFeatureSource( typeName );
if( source instanceof SimpleFeatureStore ){
 // you have write access
  SimpleFeatureStore store = (SimpleFeatureStore) source;
}
else {
 // read-only
}
在现实世界中,人们倾向于知道(或假设)他们有写访问权:
SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource( typeName );
好消息是,如果文件是只读的,这将很快导致类强制转换异常。
Use a Transaction:
有了事务,几乎所有的事情都变得更好了!在这种情况下,更好的是更快地编辑Shapefiles,一些数据类型,如WFS只允许你编辑时,当你有一个事务时。
Transaction transaction = new Transaction("Example1");
SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource( typeName );
store.setTransaction( transaction );
try {
   // perform edits here!
   transaction.commit();
}
catch( Exception eek){
   transaction.rollback();
}
Adding Data:
添加要素可以通过以下方式完成:
SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);

        SimpleFeatureType featureType = store.getSchema();

        SimpleFeatureBuilder build = new SimpleFeatureBuilder(featureType);
        GeometryBuilder geom = new GeometryBuilder();

        List<SimpleFeature> list = new ArrayList<>();
        list.add(build.buildFeature("fid1", geom.point(1, 1), "hello"));
        list.add(build.buildFeature("fid2", geom.point(2, 3), "martin"));
        SimpleFeatureCollection collection = new ListFeatureCollection(featureType, list);

        Transaction transaction = new DefaultTransaction("Add Example");
        store.setTransaction(transaction);
        try {
            store.addFeatures(collection);
            transaction.commit(); // actually writes out the features in one go
        } catch (Exception eek) {
            transaction.rollback();
        }

Hints提示:
如果addFeatures真的很慢,你可能忘了使用Transaction!
有FeatureCollection吗?
addFeatures方法真的想要一个功能集合,如果你有其他的东西,而不是一个FeatureCollection,有几个DataUtilityMethods周围的帮助:
store.addFeatures( DataUtilities.collection( feature ) );
store.addFeatures( DataUtilities.collection( array ) );
store.addFeatures( DataUtilities.collection( list ) );
store.addFeatures( DataUtilities.collection( set ) );
我说的一对夫妇是指他们都被命名为集合,只是他们愿意从一系列的输入中适应。
处理FeatureID:
每个要素都有一个标识符,该标识符与WFS规范一致,是唯一的。对于大多数实现,FeatureID是在添加特性时分配的(甚至更有趣的是,当它被提交时!):
Transaction transaction = new DefaultTransaction("Add Example");
        SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);
        store.setTransaction(transaction);
        try {
            List<FeatureId> added = store.addFeatures(collection);
            System.out.println(added); // prints out the temporary feature ids

            transaction.commit();
            System.out.println(added); // prints out the final feature ids

            Set<FeatureId> selection = new HashSet<>(added);
            FilterFactory ff = CommonFactoryFinder.getFilterFactory();
            Filter selected = ff.id(selection); // filter selecting all the features just added
        } catch (Exception problem) {
            transaction.rollback();
            throw problem;
        }
在提交过程中分配FeatureID。当我们尝试在提交之前确定一个适当的ID时,我们要求您等到commit()完成后再写下添加内容的标识符。
addFeatures返回的FeatureID实例被更新,以反映提交期间提供的最终值。如果您需要自己执行此步骤,您可以侦听BatchFeatureEvent,如下所示。
添加以下内容时发送FeatureEvents:
Transaction transaction = new DefaultTransaction("Add Example");
        SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);
        store.setTransaction(transaction);

        class CommitListener implements FeatureListener {
            public void changed(FeatureEvent featureEvent) {
                if (featureEvent instanceof BatchFeatureEvent) {
                    BatchFeatureEvent batchEvent = (BatchFeatureEvent) featureEvent;

                    System.out.println("area changed:" + batchEvent.getBounds());
                    System.out.println("created fids:" + batchEvent.getCreatedFeatureIds());
                } else {
                    System.out.println("bounds:" + featureEvent.getBounds());
                    System.out.println("change:" + featureEvent.getFilter());
                }
            }
        }
        CommitListener listener = new CommitListener();
        store.addFeatureListener(listener);
        try {
            List<FeatureId> added = store.addFeatures(collection);
            transaction.commit();
        } catch (Exception problem) {
            transaction.rollback();
            throw problem;
        }
在提交期间发送的BatchFeatureEvent包含最终的标识符集
自己处理FeatureID:
最近,一些数据存储实现(JDBCNG和Property)增加了对“Hint”的支持,允许您定义自己的FeatureID:
if( featureStore.getQueryCapabilities().isUseExizingFIDSupported() ){
    // featureStore allows us to create our own featureIDs
    SimpleFeatureBuilder b = new SimpleFeatureBuilder(featureStore.getSchema());
    DefaultFeatureCollection collection = new DefaultFeatureCollection(null,featureStore.getSchema());

    String typeName = b.getFeatureType().getTypeName();
    for( FeatureIterator iter=features.features(); iter.hasNext(); ){
        SimpleFeature feature = (SimpleFeature) iter.next();

        b.init( feature ); // take feature into a builder to modify
        b.featureUserData(Hints.USE_EXISTING_FID, Boolean.TRUE);
        feature = b.buildFeature( typeName+"."+System.currentTimeMillis() );

        collection.add( feature );
    }
    featureStore.addFeatures(collection);
}
else {
   // allow featurestore to create featureIDs
   featureStore.addFeatures( features );
}
Removing Data:
与添加数据相反的是删除,在这种情况下,我们需要使用Filter来选择要删除的特征:
 Transaction transaction = new DefaultTransaction("removeExample");
        SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);
        store.setTransaction(transaction);

        FilterFactory ff = CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints());
        Filter filter = ff.id(Collections.singleton(ff.featureId("fred")));
        try {
            store.removeFeatures(filter);
            transaction.commit();
        } catch (Exception eek) {
            transaction.rollback();
        }
这当然留下了一个明显的问题:
问:刚才删除了什么?
如果您想向用户报告删除了哪些特性,则需要在删除之前选择featureid。
Transaction transaction = new DefaultTransaction("removeExample");
        SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);
        store.setTransaction(transaction);

        FilterFactory ff = CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints());
        Filter filter = ff.id(Collections.singleton(ff.featureId("fred")));
        try {
            final Set<FeatureId> removed = new HashSet<>();
            SimpleFeatureCollection collection =
                    store.getFeatures(new Query(typeName, filter, Query.NO_NAMES));
            collection.accepts(
                    new FeatureVisitor() {
                        public void visit(Feature feature) {
                            removed.add(feature.getIdentifier());
                        }
                    },
                    null);
            store.removeFeatures(filter);
            transaction.commit();
        } catch (Exception eek) {
            transaction.rollback();
        }
Updating Data:
您还可以对匹配特定过滤器的所有数据执行批量更改。
Transaction transaction = new DefaultTransaction("Example1");
SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource( typeName );
store.setTransaction( transaction );

FilterFactory ff = CommonFactoryFinder.getFilterFactory( GeoTools.getDefaultHints() );
Filter filter = ff.id( Collections.singleton( ff.featureId("fred")));

SimpleFeatureType featureType = store.getSchema();
try {
   store.modifyFeatures( "age", Integer.valueOf(24), filter );
   transaction.commit();
}
catch( Exception eek){
   transaction.rollback();
}
上面的代码示例找到ID为fred的特性,并将其年龄更改为24岁。
SimpleFeatureLocking:
FeatureLocking遵循与web功能服务锁定相同的模型;请求基于时间的锁。锁在被释放或持续时间到期之前一直有效。
获取锁很简单:
FeatureLock lock = new FeatureLock("test", 3600);
SimpleFeatureLocking road = (SimpleFeatureLocking) data.getFeatureSource("road");
road.setFeatureLock(lock);

road.lockFeatures( filter );

System.out.println("Features lock with authorisation: "+lock.getAuthorization() );
要再次解锁这些特性,我们需要使用上面lock.getAuthorization()提供的授权。通常这些授权被存储为应用程序的一部分(作为会话的一部分),并用于在使用SimpleFeatureStore之前配置GeoTools Transaction。
Transaction t = new DefaultTransaction();

// authorisation provided by previous lockFeatures operation
road.setTransaction("A123h123sdf2");
road.modifyFeatures( filter, .... )
road.unLockFeatures( filter );

MemoryDataStore:
我们确实有一个MemoryDataStore,适合在将临时信息保存到磁盘之前将其存储在内存中。请注意,它被设置为准确地镜像位于磁盘上的信息,并且不以任何方式执行。也就是说它是有效的;并且很容易将数据塞进去。
这个实现实际上是由' ' gt-main ' '模块提供的,为了保持一致性,这里记录了它。
Create:
MemoryDataStore并不快——它是用于测试的。你问为什么它不快?因为我们使用它来严格模拟与外部服务的工作(因此它将一次又一次地复制您的数据)。
与大多数数据存储不同,我们将手工创建这个数据存储,而不是使用工厂。
MemoryDataStore memory = new MemoryDataStore();

// you are on the honour system to only add features of the same type你在荣誉系统中只能添加相同类型的功能
memory.addFeature( feature );
...
问:为什么这么慢?
它之所以缓慢,有两个原因:
它没有索引,每次访问都需要查看每个特性并对其应用过滤器
它复制每个特性(这样您就不会意外地修改事务之外的东西)
它可能会复制每个功能,以便应用额外的慢度功能。
问:给我一些更快的
gt-main datutilities提供了几种高性能的MemoryDataStore替代方案。
Examples:
使用MemoryDataStore更改内容。
感谢Mau Macros提供的以下示例:
SimpleFeatureSource alter(
            SimpleFeatureCollection collection,
            String typename,
            SimpleFeatureType featureType,
            final List<AttributeDescriptor> newTypes) {

        try {

            // Create target schema
            SimpleFeatureTypeBuilder buildType = new SimpleFeatureTypeBuilder();
            buildType.init(featureType);
            buildType.setName(typename);
            buildType.addAll(newTypes);

            final SimpleFeatureType schema = buildType.buildFeatureType();
            // Configure memory datastore
            final MemoryDataStore memory = new MemoryDataStore();
            memory.createSchema(schema);

            collection.accepts(
                    new FeatureVisitor() {
                        public void visit(Feature feature) {
                            SimpleFeatureBuilder builder = new SimpleFeatureBuilder(schema);

                            builder.init((SimpleFeature) feature);
                            for (AttributeDescriptor descriptor : newTypes) {
                                builder.add(descriptor.getDefaultValue());
                            }

                            SimpleFeature newFeature =
                                    builder.buildFeature(feature.getIdentifier().getID());
                            memory.addFeature(newFeature);
                        }
                    },
                    null);

            return memory.getFeatureSource(typename);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Exporting:
人们通常想要做的一件事是获取数据(现有数据)并导出到shapefile或PostGIS。许多桌面和服务器应用程序都可以很好地使用shapefile。
在不同格式之间获取信息时,需要记住一些技巧,我们将在本页介绍几个示例。
References:
doc:
/tutorial/geometry/geometrycrs tutorial covers transforming a shapefile

Memory:
在处理自己的应用程序时,您通常会将数据存储在正在构建的MemoryDataStore中。
下面的例子展示了如何将MemoryDataStore导出为单个shapefile:
 DataStore exportToShapefile(MemoryDataStore memory, String typeName, File directory)
            throws IOException {
        // existing feature source from MemoryDataStore
        SimpleFeatureSource featureSource = memory.getFeatureSource(typeName);
        SimpleFeatureType ft = featureSource.getSchema();

        String fileName = ft.getTypeName();
        File file = new File(directory, fileName + ".shp");

        Map<String, java.io.Serializable> creationParams = new HashMap<>();
        creationParams.put("url", URLs.fileToUrl(file));

        FileDataStoreFactorySpi factory = FileDataStoreFinder.getDataStoreFactory("shp");
        DataStore dataStore = factory.createNewDataStore(creationParams);

        dataStore.createSchema(ft);

        // The following workaround to write out the prj is no longer needed
        // ((ShapefileDataStore)dataStore).forceSchemaCRS(ft.getCoordinateReferenceSystem());

        SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);

        Transaction t = new DefaultTransaction();
        try {
            SimpleFeatureCollection collection = featureSource.getFeatures(); // grab all features
            featureStore.addFeatures(collection);
            t.commit(); // write it out
        } catch (IOException eek) {
            eek.printStackTrace();
            try {
                t.rollback();
            } catch (IOException doubleEeek) {
                // rollback failed?
            }
        } finally {
            t.close();
        }
        return dataStore;
    }
我们还有一个使用FeatureWriter的替代示例(谢谢Gaby)。
FeatureSource和FeatureCollection 是高级API,要想更深入,你可以使用低级的featereader / FeatureWriter API。
DataStore exportToShapefile2(MemoryDataStore memory, String typeName, File directory)
            throws IOException {
        // existing feature source from MemoryDataStore
        SimpleFeatureSource featureSource = memory.getFeatureSource(typeName);
        SimpleFeatureType ft = featureSource.getSchema();

        String fileName = ft.getTypeName();
        File file = new File(directory, fileName + ".shp");

        Map<String, java.io.Serializable> creationParams = new HashMap<>();
        creationParams.put("url", URLs.fileToUrl(file));

        FileDataStoreFactorySpi factory = FileDataStoreFinder.getDataStoreFactory("shp");
        DataStore dataStore = factory.createNewDataStore(creationParams);

        dataStore.createSchema(ft);

        SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);

        try (Transaction t = new DefaultTransaction()) {
            SimpleFeatureCollection collection = featureSource.getFeatures(); // grab all features

            FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
                    dataStore.getFeatureWriter(typeName, t);

            SimpleFeatureIterator iterator = collection.features();
            SimpleFeature feature;
            try {
                while (iterator.hasNext()) {
                    feature = iterator.next();

                    // Step1: create a new empty feature on each call to next
                    SimpleFeature aNewFeature = writer.next();
                    // Step2: copy the values in
                    aNewFeature.setAttributes(feature.getAttributes());
                    // Step3: write out the feature
                    writer.write();
                }

            } catch (IOException eek) {
                eek.printStackTrace();
                try {
                    t.rollback();
                } catch (IOException doubleEeek) {
                    // rollback failed?
                }
            }
        }
        return dataStore;
    }
一些提示:
Shapefile不支持“几何”,所以如果你使用混合内容,你需要导出三个Shapefile,一个点,一个线,一个多边形。
WFS:
您可以使用上述技术从WFS复制到Shapefile。
一些提示:
请注意,WFS typename并不总是有效的文件名;在生成文件名之前,你应该花点时间将非字母数字字符更改为“_”。
WFS- t协议不允许我们实现createSchema,所以在调用addFeature之前,需要根据WFS的过程来创建一个新的featureType。
作为一个例子,GeoServer支持为此目的使用REST API。
PostGIS:
将资料复制到PostGIS:
您可以使用上面的示例从PostGIS导出
PostGIS还支持createSchema,允许您创建一个新表来保存内容
您可能希望在调用createSchema之前调整featureType信息,特别是字符串的长度

CQL:
gt-cql模块是一种人类可读的“上下文查询语言”,用于编写用于处理地理空间信息的筛选表达式。
CQL最初被称为公共查询语言(因此您将发现许多仍然引用该名称的示例)。该标准来自图书馆学,并由OGC在实现其目录服务器规范时采用。
出于我们的目的,它提供了一种很好的人类可读的方式来表达过滤器,类似于SQL的“where子句”。事实上,我们有自己的扩展,允许您使用简单的文本字符串表示GeoTools的过滤器和表达式的想法的全部范围。
CQL:
CQL实用程序类提供了一个很好的前端,可以从文本字符串生成过滤器和表达式。
CQL Utility Class:
CQL工具类由静态方法组成,您可以调用这些方法将文本字符串转换为表达式、过滤器或列表<过滤器>。它还能够获取这些项并生成适当的文本表示。
以下是使用CQL to Filter解析器的示例。要全面了解谓词语言,请参阅BNF。
Running:
正如您在上面看到的,CQL类可以在命令行上运行。
它允许您尝试本页上的CQL示例;并生成结果的XML Filter表示。
Examples:
Simple example:
Filter filter = CQL.toFilter("attName >= 5");
将给你一个GeoAPI过滤器对应于:
<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml">
  <ogc:PropertyIsGreaterThanOrEqualTo>
    <ogc:PropertyName>attName</ogc:PropertyName>
    <ogc:Literal>5</ogc:Literal>
  </ogc:PropertyIsGreaterThanOrEqualTo>
</ogc:Filter>
你可以使用以下命令再次输出文本:
CQL.toCQL( filter );
解析单个表达式:
如果你想解析单个表达式而不是过滤器:
Expression expr = CQL.toExpression("attName");
您可以使用以下命令再次获得文本:
CQL.toCQL( expr );
解析过滤器列表:
甚至可以一次解析过滤器列表。这对于像GeoServer这样的应用程序很有用,例如,它允许为WMS GetMap请求中的每一层定义一个过滤器谓词。
使用“;”字符分隔出每个过滤器:
List filters = CQL.toFilterList("att1 > 5;ogc:name = 'river'");
在本例中,您将获得两个过滤器,因为“;”字符充当分隔符。
使用您自己的FilterFactory:
如果你已经有了一个工厂,没有必要强迫CQL在内部创建一个:
Filter filter = CQL.toFilter("attName >= 5", filterFactory );
Include and Exclude:
你可以表示包含和排除令牌:
String cql = "INCLUDE;EXCLUDE";
List filters = CQL.toFilterList(cql);
提示:
INCLUDE为空,表示您根本不希望应用任何约束。
EXCLUDE用于过滤所有内容。
通过比较值筛选:
Filter result = CQL.toFilter("ATTR1 < (1 + ((2 / 3) * 4))" );
Filter result = CQL.toFilter("ATTR1 < abs(ATTR2)" );
Filter result = CQL.toFilter("ATTR1 < 10 AND ATTR2 < 2 OR ATTR3 > 10" );
使用文本过滤:
Filter result = CQL.toFilter( "ATTR1 LIKE 'abc%'" );
Filter result = CQL.toFilter( "ATTR1 NOT LIKE 'abc%'" );
null过滤:
Filter result = CQL.toFilter( "ATTR1 IS NULL" );
Filter result = CQL.toFilter( "ATTR1 IS NOT NULL" );
通过比较时间值进行过滤:
等于日期的:
Filter result = CQL.toFilter( "ATTR1 TEQUALS 2006-11-30T01:30:00Z" );
在日期之前:
Before filter = (Before) CQL.toFilter("lastEarthQuake BEFORE 2006-11-30T01:30:00Z");
在日期范围之前:
Filter result = CQL.toFilter( "ATTR1 BEFORE 2006-11-30T01:30:00Z/2006-12-31T01:30:00Z" );
在日期之后:
 After filter = (After) CQL.toFilter("lastEarthQuake AFTER 2006-11-30T01:30:00Z");
在使用GMT+3时区的日期之后:
 After filter = (After) CQL.toFilter("lastEarthQuake AFTER 2006-11-30T01:30:00+03:00");
在日期范围之后:
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/2006-12-31T01:30:00Z" );
具有持续时间的时间谓词(2006-11-30T01:30:00Z后10天):
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/P10D" );
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/T10H" );
在什么之间表述:
 During filter =
                (During)
                        CQL.toFilter(
                                "lastEarthQuake DURING 1700-01-01T00:00:00/2011-01-01T00:00:00");
基于存在的过滤器:
检查是否存在:
Filter result = CQL.toFilter( "ATTR1 EXISTS" );
检查是否有东西不存在:
Filter result = CQL.toFilter( "ATTR1 DOES-NOT-EXIST" );
通过检查一个值是否在区间:
Filter result = CQL.toFilter( "ATTR1 BETWEEN 10 AND 20" );
使用复合属性:
Filter result = CQL.toFilter( "gmd:MD_Metadata.gmd:identificationInfo.gmd:MD_DataIdentification.gmd:abstract LIKE  'abc%'" );
使用几何关系过滤:
Filter result = CQL.toFilter( "CONTAINS(ATTR1, POINT(1 2))" );
Filter result = CQL.toFilter( "BBOX(ATTR1, 10,20,30,40)" );
Filter result = CQL.toFilter( "DWITHIN(ATTR1, POINT(1 2), 10, kilometers)" );
Filter result = CQL.toFilter( "CROSS(ATTR1, LINESTRING(1 2, 10 15))" );
Filter result = CQL.toFilter( "INTERSECT(ATTR1, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );
Filter result = CQL.toFilter( "CROSSES(ATTR1, LINESTRING(1 2, 10 15))" );
Filter result = CQL.toFilter( "INTERSECTS(ATTR1, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );
下面的示例展示了如何使用RELATE操作创建一个过滤器。在这种情况下,DE-9IM模式对应于包含空间关系,如果第一个几何包含第二个几何,则为真。
Filter filter =
                ECQL.toFilter(
                        "RELATE(geometry, LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");

ECQL:
ECQL语言旨在作为CQL的扩展,因此您可以编写CQL支持的所有谓词,并使用在新语法规则中定义的新表达式可能性。
从GeoTools 19.0开始,在用户数据中携带一个CoordinateReferenceSystem的几何对象被编码为EWKT。如果你不希望这样,设置提示。ENCODE_EWKT系统提示为false (e..g, Hints.putSystemDefault(Hints.ENCODE_EWKT, false);)。

ECQL Utility Class:
ECQL实用程序类是方法兼容的,允许您将其用作CQL的临时替代品。
Running:
正如您在上面看到的,ECQL类可以在命令行上运行。
它允许您尝试本页上的ECQL示例;并生成结果的XML Filter表示。
ECQL Filter Tester
"Separate with \";\" or \"quit\" to finish)
>attr > 10
<?xml version="1.0" encoding="UTF-8"?>
<ogc:PropertyIsGreaterThan xmlns="http://www.opengis.net/ogc" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml">
<ogc:PropertyName>attr</ogc:PropertyName>
<ogc:Literal>10</ogc:Literal>
</ogc:PropertyIsGreaterThan>

>quit
Bye!

Examples:
通过比较值进行筛选:
CQL语言限制我们只能针对更一般的表达式引用propertyName。
ECQL允许你在任何地方使用完整表达式:
Filter filter = ECQL.toFilter("1000 <= population");
        Filter filter =
                ECQL.toFilter(
                        "(under18YearsOld * 19541453 / 100 ) < (over65YearsOld * 19541453 / 100 )");
        Filter filter = ECQL.toFilter("population BETWEEN 10000000 and 20000000");
        Filter filter =
                ECQL.toFilter(
                        "area( Polygon((10 10, 20 10, 20 20, 10 10)) ) BETWEEN 10000 AND 30000");
通过要素ID列表的进行过滤:
Filter XML格式允许定义捕获一组featureid(通常表示一个选择)的Id Filter。
使用字符串作为id:
Filter filter = ECQL.toFilter("IN ('river.1', 'river.2')");
使用整数作为id:
Filter filter = ECQL.toFilter("IN (300, 301)");
我们尝试了几个实验,并不是所有的实验都有效,留下了以下弃用的语法:
Filter filter = ECQL.toFilter("ID IN ('river.1', 'river.2')");
基于一组值的过滤器:
以下过滤器选择拥有白银、石油或黄金作为主要矿产资源的国家:
Filter filter = ECQL.toFilter("principalMineralResource IN ('silver','oil', 'gold' )");
使用文本模式进行过滤:
使用LIKE关键字过滤文本模式:
Filter filter = ECQL.toFilter("cityName LIKE 'New%'");
关键字ILIKE不区分大小写的示例:
Filter filter = ECQL.toFilter("cityName ILIKE 'new%'");
ECQL允许你测试任意两个表达式,包括文字:
Filter filter = ECQL.toFilter("'aabbcc' LIKE '%bb%'");
按空间关系过滤:
使用完整表达式的能力也适用于空间操作,允许我们使用函数处理几何图形,如下例所示:
Filter filter = ECQL.toFilter("DISJOINT(the_geom, POINT(1 2))");
Filter filter = ECQL.toFilter("DISJOINT(buffer(the_geom, 10) , POINT(1 2))");
Filter filter = ECQL.toFilter(
        "DWITHIN(buffer(the_geom,5), POINT(1 2), 10, kilometers)");
下面的示例展示了如何使用RELATE操作创建一个过滤器。在这种情况下,DE-9IM模式对应于包含空间关系,如果第一个几何包含第二个几何,则为真。
Filter filter =
                ECQL.toFilter(
                        "RELATE(geometry, LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");
下面的变体显示相同,但使用EWKT约定在其前面加上" SRID=epsgCode; ",为几何图形提供坐标参考系统:
 Filter filter =
                ECQL.toFilter(
                        "RELATE(geometry, SRID=4326;LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");
按时间关系过滤:
时间谓词允许建立两个给定瞬间之间的关系,或瞬间与时间间隔之间的关系。在下一个示例中,期间谓词用于过滤在指定日期之间发生地震的城市:
 During filter =
                (During)
                        ECQL.toFilter(
                                "lastEarthQuake DURING 1700-01-01T00:00:00Z/2011-01-01T00:00:00Z");
在ECQL中,你可以在时态谓词的左边写一个日期时间表达式:
Filter filter = ECQL.toFilter("2006-11-30T01:00:00Z AFTER 2006-11-30T01:30:00Z");
在Before谓词中:
 Filter filter = ECQL.toFilter("2006-11-30T01:00:00Z BEFORE 2006-11-30T01:30:00Z");
在During谓词中:
Filter filter =
                ECQL.toFilter(
                        "2006-11-30T01:00:00Z DURING 2006-11-30T00:30:00Z/2006-11-30T01:30:00Z ");
下面的例子给出了一个时间谓词,它在日期时间表达式中包含UTC时区(GMT +3):
 Filter filter =
                ECQL.toFilter(
                        "2006-11-30T01:00:00+03:00 DURING 2006-11-30T00:30:00+03:00/2006-11-30T01:30:00+03:00 ");
null过滤:
Filter filter = ECQL.toFilter(" Name IS NULL");
Filter filter = ECQL.toFilter("centroid( the_geom ) IS NULL");
属性存在谓词:
Filter resultFilter = ECQL.toFilter("aProperty EXISTS");
表达式:
表达式支持不变:
Expression expr = ECQL.toExpression("X + 1");
Filter list:
过滤器列表仍然支持使用“;”分隔条目:
List<Filter> list = ECQL.toFilterList("X=1; Y<4");
带有日期文字的过滤器:
Filter filter = ECQL.toFilter("foo = 1981-06-20");
Filter filter = ECQL.toFilter("foo <= 1981-06-20T12:30:01Z");

Coverage覆盖:
gt-coverage模块提供了一种方法来构建和使用高度结构化的数值网格,例如图像数据(例如GeoTIFF格式文件)或多维矩阵数据(例如NetCDF格式文件)。
gt-coverage模块负责:
从gt-api实现覆盖接口,如GridCoverage2D和Format
连接Java Advanced Imaging, JAI Image IO和Java Image设施以及GridCoverage的地理空间思想
使用GridFormatFinder识别CLASSPATH上可用的其他格式
该模块支持使用来自各种来源的覆盖信息(即栅格)。
GridCoverage:
为了快速读取GridCoverage,我们可以使用网格覆盖格式支持。
File file = new File("test.tiff");
        AbstractGridFormat format = GridFormatFinder.findFormat(file);
        GridCoverage2DReader reader = format.getReader(file);
        GridCoverage2D coverage = reader.read(null);
如果你已经知道你有什么样的文件,你可以直接创建阅读器:
File file = new File("test.tiff");
GeoTiffReader reader = new GeoTiffReader(file, new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE));
GridCoverage2D coverage = reader.read(null);
你也可以“手工”创建GridCoverage,如果你在内存中有一个BufferedImage,你想把它放到地图上。
下面是一个从tile(包含buffereimage和ReferencedEnvelope位置)创建网格覆盖的例子:
GridCoverage2D coverage = reader.read(null);
        CoordinateReferenceSystem crs = coverage.getCoordinateReferenceSystem2D();
        Bounds env = coverage.getEnvelope();
        RenderedImage image = coverage.getRenderedImage();
Using:
一旦我们创建了GridCoverage2D,我们就可以使用这个类来执行有用的任务。许多使用需要访问类本身,但更一般的使用可以通过OpenGIS GridCoverage接口与实例交互。
GridCoverage2D提供访问:
 GridCoverageFactory factory = new GridCoverageFactory();
 GridCoverage2D coverage = factory.create("GridCoverage", bufferedImage, referencedEnvelope);
GridCovearge2D支持直接使用上述方法进行简单使用。对于更复杂的使用,我们将在下一节中介绍Operations类。
您还可以评估特定点的覆盖率,以确定在该位置存在什么值。
GridCoverage2D coverage = reader.read(null);
        // direct access
        Position position = new Position2D(crs, x, y);
        double[] sample = (double[]) coverage.evaluate(position); // assume double
        // resample with the same array
        sample = coverage.evaluate(position, sample);
RenderedImage:
我们可以简单地通过调用getRenderedImage()方法从GridCoverage2D获取AWT图像。结果是一个标准的RenderedImage。
显然,我们可以结合这两个步骤,直接从创建的网格覆盖到最终的图像:
RenderedImage ri = myGridCoverage.getView(ViewType.GEOPHYSICS).getRenderedImage();
在生成RenderedImage时,应该可以为特定的图像指定配色方案。目前,这是使用RasterSymbolizer作为渲染过程的一部分来处理的。
Advanced高级:
覆盖模块提供了一些强大的方法来使用网格覆盖的不同视图。这些用途中的许多都是由幕后的标准JAI机制支持的,有时还与引用模块的地理引用操作结合在一起。
为了方便起见,GeoTools覆盖模块提供了Operations,这个类将处理包的复杂而强大的内部机制捆绑在一起。javadocs中的包摘要开始解释内部机制。该文档页面需要进行扩展,以更好地解释覆盖范围
运行机制的基本模式如下:
1实例化一个新的Processor
2获取所需操作的参数组
3在参数组中配置各参数
4在处理器上调用doOperation(..)方法
在此过程中提供了几个配置选项。

Operations:
Operations类提供了一个默认实例,其中包含一系列在网格覆盖上执行操作的方法。我们使用静态实例Operations。它仅作为DefaultProcessor.doOperation(..)方法的方便包装,并提供类型安全性和更简单的参数列表。
当使用GeoTools实现操作时;对于任何返回Coverage的方法,我们可以安全地将它们转换为GridCoverage2D。
resample重新取样:
我们可以创建一个新的网格覆盖,这是原始网格覆盖的重新采样到一个新的图像。我们首先定义生成图像的几何参考,然后调用Operations的resample(..)方法。默认的实例。
为了方便,可以使用包络、高度和宽度来定义一个新的GridGeometry类,或者像GridCoverage2D构造函数一样,使用MathTransform和CoordinateReferencingSystem来代替包络。
然后我们可以调用resample(..)操作。
GridGeometry mygg = new GridGeometry(...);
GridCoverage2D covresample = (GridCoverage2D) Operations.DEFAULT.resample(scicov,mygg);
Georeferencing Transformation
我们可以使用resample(..)方法将任何GridCoverage转换为另一个CRS。
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:32632");
GridCoverage2D covtransformed = (GridCoverage2D) Operations.DEFAULT.resample(scicov,targetCRS);
这种地理参考转换从人们可能天真地期望的和矢量数据发生的情况向后工作。对于矢量数据,变换取原始参考系统中的位置,并计算目标参考系统中的位置。在GridCoverage系统中,我们期望在目标参考系统中有一个规则的网格。
因此,我们首先计算网格点在目标参考系统中的位置,然后计算每个网格点在原始参考系统中的等效位置,并估计原始网格在该计算位置处的值。这就是为什么我们要进行采样(…)操作的原因。
interpolate插入:
类似地,我们可以使用Operations类插入图像。在这种情况下,我们需要选择我们将使用的插值方法。
javax.media.jai.Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_BILINEAR);
GridCoverage2D covinterpol =  (GridCoverage2D) Operations.DEFAULT.interpolate(scicov, interp);
RenderedImage ri = covinterpol.getRenderedImage();
crop裁剪:
Crop操作提供了一种裁剪(切割)GridCoverage的方法,以获得由Envelope定义的子区域。
final AbstractProcessor processor = new DefaultProcessor(null);
final ParameterValueGroup param = processor.getOperation("CoverageCrop").getParameters();
GridCoverage2D coverage = ...{get a coverage from somewhere}...;
final GeneralEnvelope crop = new GeneralEnvelope( ... );
param.parameter("Source").setValue( coverage );
param.parameter("Envelope").setValue( crop );
GridCoverage2D cropped = (GridCoverage2D) processor.doOperation(param);
Available Operations:
包摘要javadoc应该包含可用操作的完整列表。不幸的是,该系统不是自动的,所以有可能创建新的操作而不记录它们。
可用操作的完整列表可以通过以下方式从标准库中获得:
final DefaultProcessor proc = new DefaultProcessor(null);
for (Operation o : proc.getOperations() ){
    System.out.println(o.getName());
    System.out.println(o.getDescription());
    System.out.println();
}
Creating new Operations:
用户可以创建自己的操作,并添加到操作系统中。
CoverageStack:
我们可以使用CoverageStack类将几个GridCoverage2D实例组合成一个覆盖的三维堆栈。
Utility classes:
GeoTools覆盖模块还提供了一些工具类代码。
SpatioTemporalCoverage:
这是另一个覆盖的包装,它允许我们使用坐标和时间值调用.evaluate(…)方法,而不是强迫我们创建适当的Position参数。

JDBC:
支持使用Java JDBC库访问要素信息数据库。
JDBC模块被用作所有JDBC /数据库支持的数据存储的基础。单独使用它不包含任何有用的功能。有用的功能包含在特定于特定数据库的模块中。
JDBCDataStore创建JDBC数据存储的过程遵循创建任何类型数据存储的常规步骤。这就是在映射中定义连接到数据库的参数,然后创建数据存储工厂。:
Map<String, Object> params = new HashMap<>();
        params.put("dbtype", "postgis");
        params.put("host", "localhost");
        params.put("port", 5432);
        params.put("schema", "public");
        params.put("database", "database");
        params.put("user", "postgres");
        params.put("passwd", "postgres");
        params.put("preparedStatements", true);
        params.put("encode functions", true);
        DataStore dataStore = DataStoreFinder.getDataStore(params);
一个重要的参数是dbtype参数,它指定要连接的数据库类型。
Using JNDI:
上面的示例说明了指定到数据库的直接连接的情况。JNDI连接也可以使用JDBCJNDIDataStoreFactory 类来指定。
Map map = new HashMap();
map.put( "dbtype", "postgis");
map.put( "jndiReferenceName", "java:comp/env/jdbc/geotools");
DataStore store =  DataStoreFinder.getDataStore(map);
Connection Pooling:
内部JDBC数据存储在创建数据库连接时使用连接池。为应用程序正确配置连接池会对性能产生深远的影响。
下面是一个例子:
map.put( "max connections", 25);
map.put( "min connections", 10);
map.put( "connection timeout", 5);
连接验证在默认情况下是开启的,在使用它之前,需要花费一些时间来确保连接仍然有效(例如,确保DBMS没有因为服务器端超时而丢弃它)。如果你想获得额外的性能,并且你确定连接永远不会被丢弃,你可以禁用连接验证:
map.put( "validating connections", false);
Connection Parameters:
所有的gt-jdbc格式都可以选择使用以下一组连接参数。你需要查阅你正在使用的插件的文档;以下内容有助于保持一致性。
其中至关重要的一点是,它们同意为每种格式使用唯一的dbtype。
连接参数详情参考https://docs.geotools.org/latest/userguide/library/jdbc/datastore.html

Render:
支持使用Java2D API呈现地理空间信息。
这很可能是您对GeoTools库感兴趣的原因-该模块最终允许您使用设置的所有数据绘制地图。
GTRenderer to draw maps:
GTRenderer渲染器是你注册整个GeoTools体验的原因;你想看地图。
GTRenderer实际上是一个接口;目前有两种实现:
StreamingRenderer -一个伟大的实现,不缓存任何东西。这个决定使它更容易理解,并允许它在不耗尽内存的情况下处理大型数据集。
ShapefileRenderer -(不支持)仅限于shapefiles,作为尝试速度改进的游乐场。然而,所有好的优化都被StreamingRenderer拾取了。
下面是如何绘制一个outputArea矩形:
GTRenderer draw = new StreamingRenderer();
draw.setMapContent(map);
draw.paint(g2d, outputArea, map.getLayerBounds() );
如果您已经完成了快速入门,那么这就是我们在许多教程中使用的JMapPane类所使用的方法。
上面示例的重要部分是GTRenderer在Java2D类Graphics2D上工作。您可以找到许多Graphics2D的实现,这些实现允许GeoTools与屏幕之外的一系列图形系统一起工作。
Swing:
你可以创建一个摇摆控件来交互式地渲染图像;我们在教程中提供了一个示例JMapPane。
GTRenderer只是一个渲染引擎-在你自己的应用程序中,你可以考虑以下想法:
尝试不同的Java 2D图形设置,如抗锯齿
使用背景线程绘制贴图,并允许您的摆动控制将贴图拉到屏幕上,以获得平滑的“滑”地图
3D:
一个谷歌暑期的代码学生把一个渲染到纹理缓冲区和使用OpenGL处理平移和缩放的例子放在一起。
就我个人而言,我会寻找一个Graphics2D实现,它是由OpenGL命令支持的,并使用GeoTools渲染成场景图形。
如果你对学生的代码感兴趣,它目前在我们保存实验的GeoTools的“spike”目录中。
Printing:
Java2D库也用于Java打印。你可以使用StreamingRenderer打印,代码像正常一样工作,只是使用打印机中的Graphics2D对象。
uDig使用此功能允许将映射直接打印到打印机。
PDF:
我们还成功地使用GTRenderer和Batik生成PDF输出(它们提供Graphics2D对象)。
您可以在uDig和GeoServer中看到这个功能。
下面的例子取自uDig:
Rectangle suggestedPageSize = getITextPageSize(page1.getPageSize());
  Rectangle pageSize = rotatePageIfNecessary(suggestedPageSize);
  //rotate if we need landscape
  Document document = new Document(pageSize);
  ...
  PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputFile));
  document.open();
  Graphics2D graphics = cb.createGraphics(pageSize.getWidth(), pageSize.getHeight());

  // call your GTRenderer here
  GTRenderer draw = new StreamingRenderer();
  draw.setMapContent(map);

  draw.paint(graphics, outputArea, map.getLayerBounds() );

  // cleanup
  graphics.dispose();

  //cleanup
  document.close();
  writer.close();
SVG:
GeoVista团队已经使用Batik项目来生成SVG输出。
您需要自己管理蜡染依赖关系,如果您的映射包含任何基于图像的图形,请确保包含蜡染编解码器。
感谢James Macgill提供的以下代码示例:
/**
     * Generate an SVG document from the supplied information. Note, use cavasSize first if you want
     * to change the default output size.
     *
     * @param map Contains the layers (features + styles) to be rendered
     * @param env The portion of the map to generate an SVG from
     * @param out Stream to write the resulting SVG out to (probable should be a new file)
     * @param canvasSize optional canvas size, will default to 300x300
     * @throws IOException Should anything go wrong whilst writing to 'out'
     * @throws ParserConfigurationException If critical XML tools are missing from the classpath
     */
    public static void exportSVG(
            MapContent map, ReferencedEnvelope env, OutputStream out, Dimension canvasSize)
            throws IOException, ParserConfigurationException {
        if (canvasSize == null) {
            canvasSize = new Dimension(300, 300); // default of 300x300
        }

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();

        // Create an instance of org.w3c.dom.Document
        Document document = db.getDOMImplementation().createDocument(null, "svg", null);

        // Set up the map
        SVGGeneratorContext ctx1 = SVGGeneratorContext.createDefault(document);
        SVGGeneratorContext ctx = ctx1;
        ctx.setComment("Generated by GeoTools2 with Batik SVG Generator");

        SVGGraphics2D g2d = new SVGGraphics2D(ctx, true);

        g2d.setSVGCanvasSize(canvasSize);

        StreamingRenderer renderer = new StreamingRenderer();
        renderer.setMapContent(map);

        Rectangle outputArea = new Rectangle(g2d.getSVGCanvasSize());
        ReferencedEnvelope dataArea = map.getMaxBounds();

        LOGGER.finest("rendering map");
        renderer.paint(g2d, outputArea, dataArea);
        LOGGER.finest("writing to file");
        try (OutputStreamWriter osw = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
            g2d.stream(osw);
        }
    }
Image:
您还可以要求Java为内存中的buffereimage创建Graphics2D。在绘制到这个映像之后,您可以将其写入磁盘。
下面是奥利弗在邮件列表上的一个例子(稍微修改了一下,使用了当前的GeoTools类):
public void saveImage(final MapContent map, final String file, final int imageWidth) {

    GTRenderer renderer = new StreamingRenderer();
    renderer.setMapContent(map);

    Rectangle imageBounds = null;
    ReferencedEnvelope mapBounds = null;
    try {
        mapBounds = map.getMaxBounds();
        double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
        imageBounds = new Rectangle(
                0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

    } catch (Exception e) {
        // failed to access map layers
        throw new RuntimeException(e);
    }

    BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);

    Graphics2D gr = image.createGraphics();
    gr.setPaint(Color.WHITE);
    gr.fill(imageBounds);

    try {
        renderer.paint(gr, imageBounds, mapBounds);
        File fileToSave = new File(file);
        ImageIO.write(image, "jpeg", fileToSave);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
Map data and display classes:
gt-render模块定义了一个新的数据结构来表示映射的内容。MapContent将地图定义为一系列层(按顺序绘制)。
MapContent:
MapContent用于捕获用于呈现的地图内容。
这些想法可以分为以下三类:
MapContent
要绘制的地图的内容。
MapViewport
表示要绘制的地图的面积。视口存储:
要绘制的区域的世界边界(以世界单位表示的空间范围);
要绘制的屏幕、图像或设备边界;
用于在世界和屏幕位置之间进行转换的坐标变换。
在实践中,您向MapViewport对象提供世界和屏幕边界,它为您计算坐标变换(请参阅地图和屏幕边界的纵横比了解更多细节)。
在定义要绘制的地图数据(如高度或时间)时,视口还可能使用水平空间范围以外的参数。MapViewport类目前不支持这种用法,但您可以为此目的派生一个子类。
Layer -表示要绘制的内容层;层保存在一个列表中,并按顺序绘制。
Examples:
提供对层信息的直接访问:
MapContent content = mapFrame.getMapContent();
if( content.layers().get(0) instanceof FeatureLayer ){
    FeatureLayer selectionLayer = (FeatureLayer) content.layers().get(0);
    selectLayer.setStyle( style );
}
缩小显示所有内容:
MapContent content = mapFrame.getMapContent();
MapViewport viewport = content.getMapViewport();
ReferencedEnvelope maxBounds = null;
CoordinateReferenceSystem mapCRS = viewport.getCoordinateReferenceSystem();
for (Layer layer : content.layers()) {
    ReferencedEnvelope dataBounds = layer.getBounds();
    CoordinateReferenceSystem dataCrs = dataBounds.getCoordinateReferenceSystem();
    if (!CRS.equalsIgnoreMetadata(dataCrs, mapCRS)) {
        dataBounds = dataBounds.transform(mapCRS, true);
    }
    if (maxBounds == null) {
        maxBounds = dataBounds;
    } else {
        maxBounds.expandToInclude(dataBounds);
    }
}
viewport.setBounds( maxBounds );
Aspect-ratio of map and screen bounds地图和屏幕边界的纵横比:
通常情况下,世界和屏幕边界的长宽比(宽度与高度的比率)会有所不同。默认情况下,MapViewport不会对此进行任何纠正(这种行为符合OGC WMS规范,该规范规定地图服务器应该尊重用户提供的边界,而不管任何失真结果)。
在许多情况下,最好让MapViewport自动校正不同的长宽比,从而避免地图显示失真。你可以这样请求这个行为:
MapViewport vp = myMapContent.getViewport();
vp.setMatchingAspectRatio( true );
使用此设置,视口将计算坐标变换,以便用户请求的世界边界位于屏幕或图像显示区域的中心。请注意,当应用了这样的修正后,视口存储的世界边界将比用户所要求的大。
Layer:
我们有不同的子类的层,每一个专门为不同种类的内容工作。
每个层实现必须提供的重要信息是:
Layer.getTitle()
Layer.setTitle( String )在图例中用于显示给用户
Layer.isVisible()
Layer.setVisible( boolean )
Layer.getuserData()应用程序用来保存信息的通用映射。这种方法更适合于用附加方法加载Layer来处理选择、错误反馈等问题。
Layer.getBounds() 层渲染的数据范围
Layer.addMapLayerListener()
Layer.removeMapLayerListener()当在交互式设置中使用时,用于通知渲染器任何更改。
有了这些想法,我们现在可以探索可以添加到地图上的不同类型的内容:
FeatureLayer:
FeatureLayer用于设置和渲染来自一个FeatureSource的信息。
如果您的信息碰巧是另一种格式,您可以使用DataUtilities 类的各种方法将信息转换为FeatureSource。这就是接受FeatureCollection 的构造函数在内部所做的事情。
GridCoverageLayer:
用于渲染GridCoverage。
请注意,以这种方式直接使用GridCoverage通常不如下面使用GridReaderLayer有效。
GridReaderLayer:
用于直接从GridCoverageReader动态渲染栅格信息。
这是一个有效的解决方案(很像FeatureSource),因为在许多情况下,可以确定正确的视觉,而无需将所有光栅读入内存:
在使用常见格式(如GeoTiff)时,放大后的文件读取量可能会受到限制。JPEG等其他格式要求每次加载整个图像。
当从栅格覆盖中缩小信息时,可以使用(如果可用)来避免读取整个文件。
GridReaderLayer的性能取决于你如何调整你的Java Advanced Imaging TileCache,以及你为准备显示数据所做的工作。
这个类由gt-wms扩展,用于呈现WMS信息。
DirectLayer:
实验:DirectLayer用于填充您自己的自定义渲染器(主要用于绘制比例尺,北箭头和网格来装饰地图)。
这个概念被认为是实验性的,目前还没有连接起来。
MapContext:
这些想法的早期草案是基于最初的OGC讨论文件:
Web地图上下文(WMS上下文)
Open Web Service Context (OWS Context)
eoTools社区积极寻求与其他项目(如OpenJUMP、uDig和degree)合作,以便在这些想法上进行合作。如果开源协作失败,我们将寻求与OGC工作组“开放Web上下文”文档形式的标准机构进行传统协作。
这些最初的概念在MapContent的以下扩展中得到了保留。
这里的关键设计差异在于单一的MapLayer是用于处理任何类型内容的通用工具(同时也会让人感到困惑,因为他们没有简单的方法来检查所使用的内容类型)。
在内部,这段代码被重构为使用MapContent / Layer和MapViewport。因此,我们不建议在新开发中使用MapContext和MapLayer。
在技术层面上,我们不再保留MapLayer的实例;相反,每个都是一个浅层包装,围绕着一个包含特定内容的层(FeatureLayer, GridReaderLayer等)。
在事件客户端代码期待一个MapLayer;该包装器根据需要重新创建,并从getLayer(int)方法返回。
以类似的方式,管理感兴趣区域的各种方法委托给MapViewport。

Styling features for display显示要素样式:
GeoTools渲染过程是您提供的受控制的样式信息。我们用来描述样式的数据结构基于OGC提供的样式层描述符(SLD)和符号编码(SE)规范。SLD规范定义了一个XML文档,您可以使用它来保存和加载样式。
本页专门提供示例,要复习这些概念,请参阅下面的参考资料。
StyleFactory:
我们有两种方法提供不同级别的标准遵从性和实用主义。
下面是这些类的一些实际例子:
StyleFactory
StyleFactory允许使用SLD标准定义的参数进行创建。
下面是一个快速创建PointSymbolizer的例子:
//
        org.geotools.api.style.StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        FilterFactory ff = CommonFactoryFinder.getFilterFactory();

        //
        // create the graphical mark used to represent a city
        Stroke stroke = sf.stroke(ff.literal("#000000"), null, null, null, null, null, null);
        Fill fill = sf.fill(null, ff.literal(Color.BLUE), ff.literal(1.0));

        // OnLineResource implemented by gt-metadata - so no factory!
        OnLineResourceImpl svg = new OnLineResourceImpl(new URI("file:city.svg"));
        svg.freeze(); // freeze to prevent modification at runtime

        OnLineResourceImpl png = new OnLineResourceImpl(new URI("file:city.png"));
        png.freeze(); // freeze to prevent modification at runtime

        //
        // List of symbols is considered in order with the rendering engine choosing
        // the first one it can handle. Allowing for svg, png, mark order
        List<GraphicalSymbol> symbols = new ArrayList<>();
        symbols.add(sf.externalGraphic(svg, "svg", null)); // svg preferred
        symbols.add(sf.externalGraphic(png, "png", null)); // png preferred
        symbols.add(sf.mark(ff.literal("circle"), fill, stroke)); // simple circle backup plan

        Expression opacity = null; // use default
        Expression size = ff.literal(10);
        Expression rotation = null; // use default
        AnchorPoint anchor = null; // use default
        Displacement displacement = null; // use default

        // define a point symbolizer of a small circle
        Graphic circle = sf.graphic(symbols, opacity, size, rotation, anchor, displacement);
        PointSymbolizer pointSymbolizer =
                sf.pointSymbolizer("point", ff.property("the_geom"), null, null, circle);
要使用GeoTools供应商特定的选项,需要一种稍微不同的编程风格,利用正在创建的可变实例,允许您调用get和set方法。
这些类不是线程安全的,当样式用于绘制时不会更新样式。
 //
        // We are using the GeoTools styleFactory that allows access to get/set methods
        org.geotools.api.style.StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        FilterFactory ff = CommonFactoryFinder.getFilterFactory();

        StyledLayerDescriptor sld = sf.createStyledLayerDescriptor();
        sld.setName("sld");
        sld.setTitle("Example");
        sld.setAbstract("Example Style Layer Descriptor");

        UserLayer layer = sf.createUserLayer();
        layer.setName("layer");

        //
        // define constraint limited what features the sld applies to
        FeatureTypeConstraint constraint =
                sf.createFeatureTypeConstraint("Feature", Filter.INCLUDE, null);

        layer.layerFeatureConstraints().add(constraint);

        //
        // create a "user defined" style
        Style style = sf.createStyle();
        style.setName("style");
        style.getDescription().setTitle("User Style");
        style.getDescription().setAbstract("Definition of Style");

        //
        // define feature type styles used to actually define how features are rendered
        FeatureTypeStyle featureTypeStyle = sf.createFeatureTypeStyle();

        // RULE 1
        // first rule to draw cities
        Rule rule1 = sf.createRule();
        rule1.setName("rule1");
        rule1.getDescription().setTitle("City");
        rule1.getDescription().setAbstract("Rule for drawing cities");
        rule1.setFilter(ff.less(ff.property("POPULATION"), ff.literal(50000)));

        //
        // create the graphical mark used to represent a city
        Stroke stroke = sf.stroke(ff.literal("#000000"), null, null, null, null, null, null);
        Fill fill = sf.fill(null, ff.literal(Color.BLUE), ff.literal(1.0));

        // OnLineResource implemented by gt-metadata - so no factory!
        OnLineResourceImpl svg = new OnLineResourceImpl(new URI("file:city.svg"));
        svg.freeze(); // freeze to prevent modification at runtime

        OnLineResourceImpl png = new OnLineResourceImpl(new URI("file:city.png"));
        png.freeze(); // freeze to prevent modification at runtime

        //
        // List of symbols is considered in order with the rendering engine choosing
        // the first one it can handle. Allowing for svg, png, mark order
        List<GraphicalSymbol> symbols = new ArrayList<>();
        symbols.add(sf.externalGraphic(svg, "svg", null)); // svg preferred
        symbols.add(sf.externalGraphic(png, "png", null)); // png preferred
        symbols.add(sf.mark(ff.literal("circle"), fill, stroke)); // simple circle backup plan

        Expression opacity = null; // use default
        Expression size = ff.literal(10);
        Expression rotation = null; // use default
        AnchorPoint anchor = null; // use default
        Displacement displacement = null; // use default

        // define a point symbolizer of a small circle
        Graphic city = sf.graphic(symbols, opacity, size, rotation, anchor, displacement);
        PointSymbolizer pointSymbolizer =
                sf.pointSymbolizer("point", ff.property("the_geom"), null, null, city);

        rule1.symbolizers().add(pointSymbolizer);

        featureTypeStyle.rules().add(rule1);

        //
        // RULE 2 Default

        List<GraphicalSymbol> dotSymbols = new ArrayList<>();
        dotSymbols.add(sf.mark(ff.literal("circle"), null, null));
        Graphic dotGraphic = sf.graphic(dotSymbols, null, ff.literal(3), null, null, null);
        PointSymbolizer dotSymbolizer = sf.pointSymbolizer("dot", null, null, null, dotGraphic);
        List<org.geotools.api.style.Symbolizer> symbolizers = new ArrayList<>();
        symbolizers.add(dotSymbolizer);
        Filter other = null; // null will mark this rule as "other" accepting all remaining features
        Rule rule2 =
                sf.rule(
                        "default",
                        null,
                        null,
                        Double.MIN_VALUE,
                        Double.MAX_VALUE,
                        symbolizers,
                        other);
        featureTypeStyle.rules().add(rule2);

        style.featureTypeStyles().add(featureTypeStyle);

        layer.userStyles().add(style);

        sld.layers().add(layer);

StyleBuilder from gt-main:
由于Style是由一组复杂的对象组成的,因此提供了StyleBuilder对象来构建简单的样式,而无需手动构建所有的样式元素。
例如,你可以创建一个PolygonSymbolizer,然后通过一个方法调用来创建一个Style:构建器将为你生成一个默认的FeatureTypeStyle和Rule。
//
        // We are using the GeoTools StyleBuilder that is helpful for quickly making things
        StyleBuilder builder = new StyleBuilder();
        FilterFactory ff = builder.getFilterFactory();

        // RULE 1
        // first rule to draw cities

        // define a point symbolizer representing a city
        Graphic city = builder.createGraphic();
        city.setSize(ff.literal(10));
        city.graphicalSymbols().add(builder.createExternalGraphic("file:city.svg", "svg")); // svg
        // preferred
        city.graphicalSymbols()
                .add(builder.createExternalGraphic("file:city.png", "png")); // png next
        city.graphicalSymbols()
                .add(builder.createMark(StyleBuilder.MARK_CIRCLE, Color.BLUE, Color.BLACK, 1));
        PointSymbolizer pointSymbolizer = builder.createPointSymbolizer(city, "the_geom");

        Rule rule1 = builder.createRule(pointSymbolizer);
        rule1.setName("rule1");
        rule1.getDescription().setTitle("City");
        rule1.getDescription().setAbstract("Rule for drawing cities");
        rule1.setFilter(ff.less(ff.property("POPULATION"), ff.literal(50000)));

        //
        // RULE 2 Default
        Graphic dotGraphic =
                builder.createGraphic(null, builder.createMark(StyleBuilder.MARK_CIRCLE), null);
        PointSymbolizer dotSymbolize = builder.createPointSymbolizer(dotGraphic);
        Rule rule2 = builder.createRule(dotSymbolize);
        rule2.setElseFilter(true);

        //
        // define feature type styles used to actually define how features are rendered
        Rule[] rules = {rule1, rule2};
        FeatureTypeStyle featureTypeStyle = builder.createFeatureTypeStyle("Feature", rules);

        //
        // create a "user defined" style
        Style style = builder.createStyle();
        style.setName("style");
        style.getDescription().setTitle("User Style");
        style.getDescription().setAbstract("Definition of Style");
        style.featureTypeStyles().add(featureTypeStyle);
StyleBuilder还通过填充许多默认值来提供帮助。使用默认值现在已经不是问题了,因为渲染系统能够正确地处理null作为默认值,例如默认符号大小。
StyleBuilder from gt-brewer:
Style style = new StrokeBuilder().color(Color.BLACK).width(3).buildStyle();
What to use:
对于使用符号编码,建议使用StyleFactory,因为它定义了少量易于使用的方法。然而,没有有用的方法和捷径(但你有更少的方法绊倒的优势)。因为一切都是显而易见的,你可能会发现一些棘手的高级功能,可能不明显使用StyleBuilder。
对于使用样式层描述符,使用StyleBuilder快速创建对象,并填充其默认值;然后根据需要使用setter对它们进行配置。
在内部我们有:
StyleFactoryImpl2创建原始对象的
StyleFactoryImpl使用委托来创建对象;然后允许由gt-api StyleFactory定义的更广泛的创建方法
StyleBuilder使用一个FilterFactory和一个StyleFactory来构建一个复杂的数据结构

Style Layer Descriptor:
GeoTools样式建立在样式层描述符数据模型上,如下所示(来自gt-api)。
GeoTools渲染倾向于关注“用户样式”,我们表示样式,让您控制如何渲染您的地图。
风格
Style:
Style接口与“样式层描述符”1.0规范相匹配(因此,如果您需要解释或示例,请查看OGC文档以获取更多信息)。
Create:
使用StyleFactory创建一个styelayerdescriptor对象:
StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();

        StyledLayerDescriptor sld = styleFactory.createStyledLayerDescriptor();
        sld.setName("example");
        sld.setAbstract("Example Style Layer Descriptor");

        UserLayer layer = styleFactory.createUserLayer();
        layer.setName("layer");

        FeatureTypeConstraint constraint =
                styleFactory.createFeatureTypeConstraint("Feature", Filter.INCLUDE, null);

        layer.layerFeatureConstraints().add(constraint);

        Style style = styleFactory.createStyle();

        style.getDescription().setTitle("Style");
        style.getDescription().setAbstract("Definition of Style");

        // define feature type styles used to actually
        // define how features are rendered
        //
        layer.userStyles().add(style);

        sld.layers().add(layer);
这是我们最后一次讨论styelayerdescriptor对象——它在控制渲染过程中并不是很有用。
Access:
从一个styelayerdescriptor对象变成一些有用的东西:
FeatureTypeStyle useful[] = SLD.featureTypeStyles( sld );
或者找到一个与你的特征类型兼容的样式:
FeatureTypeStyle applicable = SLD.featureTypeStyle( sld, schema );
Document:
样式层描述符参考文档OpenGIS标准定义了一个XML文档,我们使用它来持久化我们的GeoTools样式对象。这个标准在功能上是权威的定义,如果你发现我们有任何地方不符合要求,请给我们发一个bug报告。

How to parse an SLD file:
1、你可以使用SLD文档(一种由样式层描述符1.0规范定义的XML文件格式)创建样式:
 // create the parser with the sld configuration
        Configuration configuration = new org.geotools.sld.SLDConfiguration();
        Parser parser = new Parser(configuration);
        // the xml instance document above
        InputStream xml = new FileInputStream("markTest.sld");
// parse
        StyledLayerDescriptor sld = (StyledLayerDescriptor) parser.parse(xml);
2、SAX StyleReader:
基于SAX的styereader也可用于GeoTools 2.2代码:
private Style loadStyleFromXml() throws Exception {
    java.net.URL base = getClass().getResource("rs-testData");

    StyleFactory factory = StyleFactory.createStyleFactory();
    java.net.URL surl = new java.net.URL(base + "/markTest.sld");

    //A class to read and parse an SLD file based on verion 0.7.2 of the OGC
    SLDStyle stylereader = new SLDStyle(factory, surl);
    Style[] style = stylereader.readXML();

    return style[0];
}

How to write a SLD file:
GeoTools有一个XML传输,允许你生成一个SLD文件:
SLDTransformer styleTransform = new SLDTransformer();
String xml = styleTransform.transform(sld);
如何写一个SLD文件只使用一个样式
上面的代码示例需要一个完整的StyleLayerDescriptor文档来创建一个有效的SLD文件。
下面是你如何包装输出的Style对象:
StyledLayerDescriptor sld = styleFactory.createStyledLayerDescriptor();
UserLayer layer = styleFactory.createUserLayer();
layer.setLayerFeatureConstraints(new FeatureTypeConstraint[] {null});
sld.addStyledLayer(layer);
layer.addUserStyle(style);

SLDTransformer styleTransform = new SLDTransformer();
String xml = styleTransform.transform(sld);
同样的样式可以使用StyleBuilder来创建:
StyleBuilder sb = new StyleBuilder();
        FilterFactory ff = sb.getFilterFactory();
        Style style = sb.createStyle();
        style.setName("MyStyle");

        // "testPoint" feature type style
        Mark testMark =
                sb.createMark(sb.attributeExpression("name"), sb.createFill(Color.RED, 0.5), null);
        Graphic graph =
                sb.createGraphic(
                        null,
                        new Mark[] {testMark},
                        null,
                        sb.literalExpression(1),
                        sb.attributeExpression("size"),
                        sb.attributeExpression("rotation"));
        style.featureTypeStyles()
                .add(sb.createFeatureTypeStyle("testPoint", sb.createPointSymbolizer(graph)));

        // "labelPoint" feature type style
        AnchorPoint anchorPoint =
                sb.createAnchorPoint(sb.attributeExpression("X"), sb.attributeExpression("Y"));
        PointPlacement pointPlacement =
                sb.createPointPlacement(anchorPoint, null, sb.literalExpression(0));
        TextSymbolizer textSymbolizer =
                sb.createTextSymbolizer(
                        sb.createFill(Color.BLACK),
                        new Font[] {sb.createFont("Lucida Sans", 10), sb.createFont("Arial", 10)},
                        sb.createHalo(),
                        sb.attributeExpression("name"),
                        pointPlacement,
                        null);
        Mark circle = sb.createMark(StyleBuilder.MARK_CIRCLE, Color.RED);
        Graphic graph2 = sb.createGraphic(null, circle, null, 1, 4, 0);
        PointSymbolizer pointSymbolizer = sb.createPointSymbolizer(graph2);
        style.featureTypeStyles()
                .add(sb.createFeatureTypeStyle("labelPoint", textSymbolizer, pointSymbolizer));
作为扩展,GeoTools支持定义一个样式背景对象,该对象将在样式呈现指令应用于功能和覆盖之前用于填充画布。
下面是如何设置背景样式:
 // create a "user defined" style
        StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        Style style = sf.createStyle();
        style.setName("style");
        // ...

        //
        // set a background
        FilterFactory ff = CommonFactoryFinder.getFilterFactory();
        Fill background = sf.fill(null, ff.literal(Color.RED), ff.literal(1.0));
        style.setBackground(background);

        // ... set the feature type styles and symbolizers

Symbology Encoding图形符号编码:
特征类型样式数据模型捕获描述如何在屏幕上绘制特征的符号编码信息,并将表示我们的大部分示例。
FeatureTypeStyle:
一个FeatureTypeStyle声明了一个样式的一部分,这个样式是专门针对一个featuretypetype的,也就是说,只有当它们的FeatureType与FeatureTypeStyle中声明的FeatureType相同或其后代时,功能才会根据这个FeatureTypeStyle呈现。
当定义一个样式时,你将花费大部分时间来处理FeatureTypeStyle。FeatureTypeStyle是专门用于绘制要素的。
细节的层次类似于CSS,因为你需要定义一些规则来说明何时绘制,以及一些符号来说明如何绘制。单个符号器将使用表达式来访问特征内容(例如,TextSymbolizer将使用您提供的表达式来构造要显示的文本)。
FeatureTypeStyle.getName():机器可读名称
FeatureTypeStyle.getDescriptor():人类可读的标题和描述
FeatureTypeStyle.featureTypeNames():这里的Name很重要;它必须与您想要绘制的要素相匹配。
只有当它们的特征类型名称与记录在特征类型或后代中的特征类型名称匹配时,特征才会根据特征类型呈现。
对于大多数实际用途,您将设置featureTypeName为“Feature”,以充当通配符。
FeatureTypeStyle.semanticTypeIdentifiers():用于根据向量数据的类型(点,线或多边形)快速限制
下面是一个使用PointSymbolizer绘制任何“Feature”的快速示例:
StyleBuilder styleBuilder = new StyleBuilder();
        Style style = styleBuilder.createStyle();

        PointSymbolizer pointSymbolizer = styleBuilder.createPointSymbolizer();

        Graphic graphic = styleBuilder.createGraphic();
        ExternalGraphic external =
                styleBuilder.createExternalGraphic("file:///C:/images/house.gif", "image/gif");
        graphic.graphicalSymbols().add(external);
        graphic.graphicalSymbols().add(styleBuilder.createMark("circle"));

        pointSymbolizer.setGraphic(graphic);

        Rule rule = styleBuilder.createRule(pointSymbolizer);
        FeatureTypeStyle featureTypeStyle = styleBuilder.createFeatureTypeStyle("Feature", rule);
        style.featureTypeStyles().add(featureTypeStyle);
PointSymbolizer将如何首先尝试使用C:\images\house.gif(即外部图形),如果失败,它将使用圆圈(即标记)。

要素处理说明:
每个应用的FeatureTypeStyle都会被使用。这意味着,图层将被多次绘制,如果样式包含多个FeatureTypeStyle并且FeatureTypeStyle与要素中的FeatureType匹配。
FeatureTypeStyle是按顺序绘制的:只有在列表中的前一个被完全绘制后,一个FeatureTypeStyle才会被绘制
然后将每个特征传递给规则及其符号列表
这意味着,如果有多个规则与一个要素匹配,或者规则包含多个符号,那么一个要素可以被绘制多次。
在考虑下一个要素之前,当前FeatureTypeStyle中的全部规则和符号会应用于当前要素。
最后一个考虑是很重要的,例如,当你需要画一条双线的道路时,比如一条宽的黑线在一条细的白线下面。这可以使用两个FeatureTypeStyles,因为使用带有两个符号的Rule会生成一个在十字路口不好看的地图。
带有两个FeatureTypeStyles的样式示例:
StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
        FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory();

        Style style = styleFactory.getDefaultStyle();

        // Feature type style 1
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();
        fts.featureTypeNames().add(new NameImpl("feature-type-1"));
        style.featureTypeStyles().add(fts);

        // Feature type style 2
        FeatureTypeStyle fts2 = styleFactory.createFeatureTypeStyle();
        fts2.featureTypeNames().add(new NameImpl("feature-type-2"));

        // creating the rule 1
        Rule rule1 = styleFactory.createRule();
        rule1.setName("rule1");
        Filter aFilter = filterFactory.id(Collections.singleton(filterFactory.featureId("FID")));
        rule1.setFilter(aFilter);
        fts2.rules().add(rule1);

        // creating the rule 2
        Rule rule2 = styleFactory.createRule();
        rule2.setElseFilter(true);
        rule2.setName("rule2");
        fts2.rules().add(rule2);

        style.featureTypeStyles().add(fts2);
FeatureTypeStyle包含特定于GeoTools渲染引擎的供应商选项:
composite (source-over):
允许混合颜色控制 (使用 copy destination, source-over, destination-over, source-in, destination-in, source-out, destination-out source-atop, destination-atop, xor, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion).
omposite-base (false):组合组的定义
// multiply buffer from feature type style 0, onto composite-base provided by feature type style 1
style.featureTypeStyles().get(0).getOptions().put("composite","multiply, 0.5");
style.featureTypeStyles().get(1).getOptions().put("composite-base", "true");
firstMatch: 
在第一次匹配之后停止规则计算(使其更容易处理按不同属性值分类内容的数据集)
// exit rules on first match, like a switch statement
fts.getOptions().put("ruleEvaluation", "first");
sortBy:
检索控制顺序特征,控制绘图顺序。
语法是Attribute1 {A|D},Attribute2 {A|D}…其中A是升序,D是降序。排序方向是可选的,如果未指定,默认为升序。
// sort newer cities first, than by name
fts.getOptions().put( "sortBy", "year D,name A");

Rule:
FeatureTypeStyle包含一个或多个规则,这些规则是按照“else”规则被用来呈现任何剩余要素的可能性来考虑的。
规则的基础是:
minimum/maximum scale: 最小/最大比例尺:如果设置了当前比例尺,并且当前比例尺不在指定范围内,则该规则将不适用,因此它的符号将不会被使用
Filter:过滤器:则只有匹配过滤器的要素才会根据规则符号进行绘制
作为一种选择,该规则可以有一个“else过滤器”。这种特殊的过滤器捕获了以前的规则中没有符号化的所有特征。
FeatureTypeStyle使用featureTypename来分类我们正在处理的要素类型。Rules规则用于细化这些内容,可能根据要素属性或比例尺进行过滤,以确定我们要绘制的具体内容。
注意:
minimum/maximum scale最小和最大地图比例尺,如果设置并且当前比例尺在指定范围之外,则规则将不适用,因此其符号将不被使用
Filter过滤器用于选择要绘制的要素,只有匹配过滤器的要素才会被绘制
Rule规则可以有一个“else过滤器”。这种特殊的过滤器捕获了以前的规则(使用常规过滤器)还没有符号化的所有要素。
一旦FeatureTypeStyle和Rules确定要绘制一个Feature;该规则使用一系列Symbolizers来定义内容的绘制方式:
Symbolizer描述了如何基于特征内容(几何形状和属性)在屏幕上表示特征。
Rule每个规则都有一个Symbolizer符号列表。
Symbolizers符号像一种显示语言一样被用来在显示设备上产生像素。

Symbolizer符号:
一个符号描述了一个要素应该如何出现在地图上。每个规则都有一个按顺序应用的符号列表。
正如你所看到的,有很多种类的符号,用于点,线,多边形,标签和栅格数据。
你不需要将符号器与特征中包含的特定几何形状相匹配,渲染器将根据具体情况尝试做最合适的事情。例如,TextSymbolizer适用于各种几何图形,并将在地图上生成标签。如果你将PolygonSymbolizer应用于一条线,这条线将被关闭以形成一个多边形,然后将应用多边形符号器。
与基本标准相比,GeoTools Symbolizer接口提供了几个优势:
getGeometry()
setGeometry( Expression )
使用表达式定义几何的能力允许在考虑渲染之前使用函数对几何进行预处理。
这有点棘手(因为像buffer这样的函数会使你的几何体更大),但结果是值得的,因为它提供了大量的灵活性。
关于符号symbolizers使用的说明:
符号符不仅描述了应该出现的形状,还描述了诸如颜色和不透明度等图形属性;
符号器确实有默认行为,在创建符号器之后,您应该提供参数来覆盖默认设置;
该对象的原始细节来自于OGC样式层描述符报告(OGC 01-077) 0.7.2版本。
Renderers渲染器可以在显示样式特征时使用此信息。虽然必须记住,并不是所有的渲染器都能像这个界面所设置的那样完全表示笔画。例如,可能不支持不透明度。
图形参数及其值来自SVG/CSS2标准,其名称和语义尽可能接近。
这里需要注意的最重要的一点是,符号组件对象是由Expression对象组成的,这意味着它们可能依赖于Feature属性。
例如,您可以创建一个数学表达式,将某些Feature属性链接到线宽。
因此,你有两种方式用不同的风格来象征不同的特征:
通过对不同的过滤器使用多个规则,然后使用文字表达式构建符号器。这是创建分类映射的好方法,其中颜色、线条样式等取决于属性值所属的范围。
通过直接链接符号属性到属性值;
适用于任何符号symbolizer的配套选项:
composite (source-over): 允许控制混合颜色(using copy destination, source-over, destination-over, source-in, destination-in, source-out, destination-out source-atop, destination-atop, xor, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion).

Point Symbolizer点符号:
用于绘制点位置,实际绘制的图形被称为标记,可以选择使用一些众所周知的标记(圆形,方形等)或您自己的外部图形,如PNG图标。
Examples:
使用StyleBuilder创建PointSymbolizer的快速示例:
// "testPoint" feature type style
        StyleBuilder sb = new StyleBuilder();
        FilterFactory ff = sb.getFilterFactory();

        Mark testMark =
                sb.createMark(sb.attributeExpression("name"), sb.createFill(Color.RED, 0.5), null);
        Graphic graph =
                sb.createGraphic(
                        null, // An external graphics if needed
                        new Mark[] {testMark}, // a Mark if not an external graphics
                        null, // aSymbol
                        ff.literal(1), // opacity
                        ff.property("size"), // read from feature "size" attribute
                        ff.property("rotation")); // rotation, here read into the feature
        PointSymbolizer aPointSymbolizer = sb.createPointSymbolizer(graph);

        // creation of the style
        Style style = sb.createStyle(aPointSymbolizer);
点符号支持VendorOptions:
labelObstacle(true/false): 没有标签应该重叠此功能,用于确保点图形清晰可见,不被文本遮挡。
fallbackOnDefaultMark(true/false):如果符号器中使用的图形无法找到,则退回到默认的灰色正方形(true,默认值)或跳过符号器而不绘制任何内容(false)。

LineSymbolizer:
用于控制线(或边)的绘制方式。
Examples:

PolygonSymbolizer:
用于控制实体形状的绘制方式。
Examples:
使用StyleBuilder创建PolygonSymbolizer的快速示例:
StyleBuilder styleBuilder = new StyleBuilder();
        FilterFactory ff = CommonFactoryFinder.getFilterFactory();

        PolygonSymbolizer polygonSymbolizer = styleBuilder.createPolygonSymbolizer(Color.BLUE);
        polygonSymbolizer.getFill().setOpacity(ff.literal(0.5)); // 50% blue

        polygonSymbolizer.setStroke(styleBuilder.createStroke(Color.BLACK, 2.0));

        // will create a default feature type style and rule etc...
        Style style = styleBuilder.createStyle(polygonSymbolizer);

TextSymbolizer:
用于控制标签系统;标签是由TextSymbolizers生成的,并被扔进渲染引擎,它检测重叠,根据你定义的优先级排序,并决定最终的标签位置。
这使得TextSymbolizer有点奇怪,因为它并不总是在标签如何逐像素渲染的基础上获得最终决定权。
Examples:
下面是一个用StyleBuilder快速创建TextSymbolizer的例子:
// "labelPoint" feature type style
        StyleBuilder sb = new StyleBuilder();
        FilterFactory ff = sb.getFilterFactory();

        // creation of the TextSymbolizer
        AnchorPoint anchorPoint =
                sb.createAnchorPoint(sb.attributeExpression("X"), sb.attributeExpression("Y"));
        PointPlacement pointPlacement =
                sb.createPointPlacement(anchorPoint, null, sb.literalExpression(0));
        TextSymbolizer textSymbolizer =
                sb.createTextSymbolizer(
                        sb.createFill(Color.BLACK),
                        new Font[] {sb.createFont("Lucida Sans", 10), sb.createFont("Arial", 10)},
                        sb.createHalo(),
                        sb.attributeExpression("name"),
                        pointPlacement,
                        null);

        // creation of the Point symbolizer
        Mark circle = sb.createMark(StyleBuilder.MARK_CIRCLE, Color.RED);
        Graphic graph2 = sb.createGraphic(null, circle, null, 1, 4, 0);
        PointSymbolizer pointSymbolizer = sb.createPointSymbolizer(graph2);

        // creation of the style
        Style style = sb.createStyle();
        FeatureTypeStyle featureTypeStyle =
                sb.createFeatureTypeStyle("labelPoint", textSymbolizer, pointSymbolizer);
        style.featureTypeStyles().add(featureTypeStyle);

        // creation of the style
提供了相当多的供应商选项来使用TextSymbolizers:
allowOverruns(false): 
autoWrap(400): 
conflictResolution(true):
followLine(true): 
forceLeftToRight(true): 
goodnessOfFit(0.5): 
graphic-margin(10):
graphic-resize(true): 
group(false): 
labelAllGroup(false):
repeat(0):
maxAngleDelta(90): 
maxDisplacement(0):
minGroupDistance(3): 
partials(true): 
polygonAlign(true): 
spaceAround(50): 
charSpacing(0):
wordSpacing(0): 
underlineText(true): 
strikethroughText(true): 
kerning(true): 
displacementMode:
fontShrinkSizeMin(0): 
graphicPlacement(label):

Raster Symbolizer:
用于控制栅格数据的渲染与完整的“彩色地图”控件。

StyleVisitor:
就像使用FilterVisitor接口一样,我们将使用这些实现在嵌套的数据结构上导航,并复制我们看到的内容,或者在我们前进的过程中修改它。
要使用StyleVisitor,可以使用accept方法将其传递给Style(或任何Style对象):
style.accepts( styleVisitor );
你会发现并不是所有的Style对象都接受StyleVisitor;比如字体就没有。这并不是一个真正的问题——但在写你自己的访问者时,这是要记住的。
Ready to Use Implementations准备使用的实现:
有许多现成的实现;虽然我们在本页上提供了一些示例,但请探索库中可用的内容-您可以通过检查javadocs快速完成此操作。
StyleAttributeExtractor -返回此样式所提到的所有属性;由渲染器在构造查询时使用
DuplicatingStyleVisitor - 返回样式的副本
RescaleStyleVisitor - 返回修改后以不同比例显示的样式副本。
Implementation Tips:
如果你习惯了简单的访问列表,比如数据结构,你会很惊讶- StyleVisitor不会自己导航Style对象结构,你将不得不做这项工作。
    ...
    public void visit(Halo halo) {
        // do your work here

        // make sure you visit the "child" objects
        if( halo.getFill() != null ) halo.getFill().accepts( this );
        if( halo.getRadius() != null ) halo.getRadius().accepts( this );
    }
    ...
}
我们应该有一个AbstractStyleVisitor供您开始;你愿意帮我们写吗?
DuplicatingStyleVisitor:
DuplicatingStyleVisitor将复制任何样式对象;它跟踪使用内部堆栈复制的内容(这意味着它不是线程安全的!):
DuplicatingStyleVisitor xerox = new DuplicatingStyleVisitor();
style.accepts( xerox );
Style copy = (Style) xerox.getCopy();
请注意,这适用于所有内容:
DuplicatingStyleVisitor xerox = new DuplicatingStyleVisitor();
lineSymbolizer.accepts( xerox );
LineSymbolizer copy = (LineSymbolizer) xerox.getCopy();

RescaleStyleVisitor:
RescaleStyleVisitor可以用来扩展所提供的样式;打印时有用的东西。SLD规范在任何时候都非常小心处理像素(当你切换到300 DPI时,这很烦人)。
RescaleStyleVisitor scale = new RescaleStyleVisitor(5.0);
style.accepts( scale );
Style bigger = (Style) scale.getCopy();
请注意,这也返回一个副本;虽然你可以使用访问器修改样式,但我们发现线程问题的生命太短暂了。
RescaleStyleVisitor scale = new RescaleStyleVisitor(5.0);
lineSymbolizer.accepts( scale );
LineSymbolizer bigger = (LineSymbolizer) scale.getCopy();

Icon:
gt-render模块提供了两个工厂,可以用来告诉渲染系统有关自定义图标的信息。
注意:这被认为是高级材料,因为你需要注册你创建的工厂。
MarkFactory:
这个类的工作是计算提供的表达式(它应该计算成一个字符串),并从中构造一个好的Java形状,我们可以在地图上绘制。
ExternalGraphicsFactory:
这个工厂更有能力,因为它不局限于创造一个简单的形状。

WKT Marks:
使用WKT标记(在您的SLD中使用WKT://作为前缀)允许以一种简单的方式向呈现过程添加复杂的符号。也有从WKT几何图形构建渲染标记的选项;此外,一些java生成的形状可以用来绘制气象符号。我们希望气象预报员会发现这有用。


 

相关文章:

  • 雅典娜Athena-signa音频算法源码与麦克风阵列角度定义互换问题
  • 【uniapp小程序】如何根据开发和发行,自动替换不同环境的baseUrl
  • strings
  • RocketMQ源码 Broker-SubscriptionGroupManager 订阅组管理组件源码分析
  • fastapi-amis-admin快速创建一个后台管理系统增加音乐管理功能(3)
  • 前端接入若依后,页面白屏问题排查
  • 玩转大数据11:数据可视化与交互式分析
  • scala编码
  • 《算法面试宝典》--机器学习常见问题汇总
  • [ndss 2023]确保联邦敏感主题分类免受中毒攻击
  • “新华三杯”第十届成都信息工程大学ACM程序设计竞赛(同步赛)L. 怎么走啊(最短路+二分 分段函数)
  • 视频监控案例分析
  • Jenkins离线安装部署教程简记
  • SQL Server ,使用递归查询具有层级关系的数据。
  • 家政预约小程序带商城,图文详解
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Django 博客开发教程 8 - 博客文章详情页
  • Git 使用集
  • happypack两次报错的问题
  • iOS小技巧之UIImagePickerController实现头像选择
  • Js基础——数据类型之Null和Undefined
  • mysql 5.6 原生Online DDL解析
  • Odoo domain写法及运用
  • Sublime text 3 3103 注册码
  • vue-router的history模式发布配置
  • Yeoman_Bower_Grunt
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 构建工具 - 收藏集 - 掘金
  • 关于List、List?、ListObject的区别
  • 类orAPI - 收藏集 - 掘金
  • 前端相关框架总和
  • 提醒我喝水chrome插件开发指南
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • HanLP分词命名实体提取详解
  • 阿里云API、SDK和CLI应用实践方案
  • 昨天1024程序员节,我故意写了个死循环~
  • ​queue --- 一个同步的队列类​
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (3)STL算法之搜索
  • (libusb) usb口自动刷新
  • (二十四)Flask之flask-session组件
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (十)c52学习之旅-定时器实验
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (四)linux文件内容查看
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转)Sublime Text3配置Lua运行环境
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • ******IT公司面试题汇总+优秀技术博客汇总
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • .net 微服务 服务保护 自动重试 Polly
  • .NetCore项目nginx发布
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • [ 网络基础篇 ] MAP 迈普交换机常用命令详解
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...