-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
FeatureLayerHelper.java
200 lines (172 loc) · 7.5 KB
/
FeatureLayerHelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2008-2011, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.swing.tool;
import java.util.Collection;
import org.geotools.api.data.FeatureSource;
import org.geotools.api.data.Query;
import org.geotools.api.feature.Feature;
import org.geotools.api.feature.type.GeometryDescriptor;
import org.geotools.api.feature.type.Name;
import org.geotools.api.feature.type.PropertyDescriptor;
import org.geotools.api.filter.Filter;
import org.geotools.api.filter.FilterFactory;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.Position2D;
import org.geotools.geometry.jts.Geometries;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
/**
* Helper class used by {@linkplain InfoTool} to query vector features in a {@linkplain
* org.geotools.map.FeatureLayer}.
*
* @author Michael Bedward
* @since 2.6
* @version $URL$
*/
public class FeatureLayerHelper extends InfoToolHelper {
/**
* Default distance fraction used with line and point features. When the user clicks on the map,
* this tool searches for features within a rectangle of width w centred on the mouse location,
* where w is the average map side length multiplied by the value of this constant.
*/
public static final double DEFAULT_DISTANCE_FRACTION = 0.01d;
private static final GeometryFactory geometryFactory =
JTSFactoryFinder.getGeometryFactory(null);
private static final FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null);
private String attrName;
private Geometries geomType;
/** No argument constructor required by the helper lookup system. */
public FeatureLayerHelper() {}
/**
* {@inheritDoc} The {@code layer} argument must be an instance of {@linkplain FeatureLayer}.
*/
@Override
public void setLayer(Layer layer) {
if (!(layer instanceof FeatureLayer)) {
throw new IllegalArgumentException("layer must be an instance of FeatureLayer");
}
super.setLayer(layer);
GeometryDescriptor geomDesc = layer.getFeatureSource().getSchema().getGeometryDescriptor();
attrName = geomDesc.getLocalName();
@SuppressWarnings("unchecked")
Class<? extends Geometry> geomClass =
(Class<? extends Geometry>) geomDesc.getType().getBinding();
geomType = Geometries.getForBinding(geomClass);
}
@Override
public boolean isSupportedLayer(Layer layer) {
return layer instanceof FeatureLayer;
}
@Override
public InfoToolResult getInfo(Position2D pos) throws Exception {
InfoToolResult result = new InfoToolResult();
if (isValid()) {
Filter filter = null;
if (geomType == Geometries.POLYGON || geomType == Geometries.MULTIPOLYGON) {
Geometry posGeom = createSearchPoint(pos);
filter =
filterFactory.intersects(
filterFactory.property(attrName), filterFactory.literal(posGeom));
} else {
ReferencedEnvelope env = createSearchEnv(pos);
filter = filterFactory.bbox(filterFactory.property(attrName), env);
}
Query query = new Query(null, filter);
query.setCoordinateSystemReproject(getMapContent().getCoordinateReferenceSystem());
FeatureSource featureSource = getLayer().getFeatureSource();
Collection<PropertyDescriptor> descriptors = featureSource.getSchema().getDescriptors();
FeatureCollection queryResult = featureSource.getFeatures(query);
try (FeatureIterator iter = queryResult.features()) {
while (iter.hasNext()) {
Feature f = iter.next();
result.newFeature(f.getIdentifier().getID());
for (PropertyDescriptor desc : descriptors) {
Name name = desc.getName();
Object value = f.getProperty(name).getValue();
if (value != null) {
if (value instanceof Geometry) {
result.setFeatureValue(name, value.getClass().getSimpleName());
} else {
result.setFeatureValue(name, value);
}
} else {
result.setFeatureValue(name, "null");
}
}
}
}
}
return result;
}
/**
* Converts the query position, in map content coordinates, to a position in layer coordinates
* and returns it as a JTS {@code Point}.
*
* @param pos query position in map content coordaintes
* @return point in layer coordinates
*/
private Geometry createSearchPoint(Position2D pos) {
try {
Position2D trPos = new Position2D();
getContentToLayerTransform().transform(pos, trPos);
Geometry point = geometryFactory.createPoint(new Coordinate(trPos.x, trPos.y));
return point;
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
private ReferencedEnvelope createSearchEnv(Position2D pos) {
ReferencedEnvelope mapBounds = getMapContent().getViewport().getBounds();
if (mapBounds == null || mapBounds.isEmpty()) {
// fall back to layer bounds
Layer layer = getLayer();
if (layer == null) {
// this should never happen
throw new IllegalStateException("Target layer has been lost");
}
mapBounds = getLayer().getBounds();
}
double halfWidth =
0.5 * DEFAULT_DISTANCE_FRACTION * (mapBounds.getWidth() + mapBounds.getHeight());
CoordinateReferenceSystem contentCRS = getMapContent().getCoordinateReferenceSystem();
ReferencedEnvelope env =
new ReferencedEnvelope(
pos.x - halfWidth,
pos.x + halfWidth,
pos.y - halfWidth,
pos.y + halfWidth,
contentCRS);
if (isTransformRequired()) {
CoordinateReferenceSystem layerCRS =
getLayer().getFeatureSource().getSchema().getCoordinateReferenceSystem();
try {
env = env.transform(layerCRS, true);
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
return env;
}
}