1 package org
.argeo
.geotools
.jcr
;
3 import java
.io
.IOException
;
4 import java
.util
.Collections
;
6 import java
.util
.HashMap
;
7 import java
.util
.HashSet
;
10 import java
.util
.concurrent
.Executor
;
12 import javax
.jcr
.Node
;
13 import javax
.jcr
.Property
;
14 import javax
.jcr
.RepositoryException
;
15 import javax
.jcr
.Session
;
16 import javax
.jcr
.observation
.Event
;
17 import javax
.jcr
.observation
.EventIterator
;
18 import javax
.jcr
.observation
.EventListener
;
19 import javax
.jcr
.observation
.ObservationManager
;
21 import org
.apache
.commons
.logging
.Log
;
22 import org
.apache
.commons
.logging
.LogFactory
;
23 import org
.argeo
.ArgeoException
;
24 import org
.argeo
.geotools
.GeoToolsUtils
;
25 import org
.argeo
.jcr
.JcrUtils
;
26 import org
.argeo
.jcr
.gis
.GisNames
;
27 import org
.argeo
.jcr
.gis
.GisTypes
;
28 import org
.argeo
.jts
.jcr
.JtsJcrUtils
;
29 import org
.geotools
.data
.DataStore
;
30 import org
.geotools
.data
.DefaultTransaction
;
31 import org
.geotools
.data
.FeatureStore
;
32 import org
.geotools
.data
.Transaction
;
33 import org
.geotools
.feature
.FeatureCollection
;
34 import org
.geotools
.feature
.FeatureCollections
;
35 import org
.geotools
.feature
.simple
.SimpleFeatureBuilder
;
36 import org
.geotools
.feature
.simple
.SimpleFeatureTypeBuilder
;
37 import org
.geotools
.filter
.FilterFactoryImpl
;
38 import org
.geotools
.referencing
.CRS
;
39 import org
.opengis
.feature
.simple
.SimpleFeature
;
40 import org
.opengis
.feature
.simple
.SimpleFeatureType
;
41 import org
.opengis
.filter
.FilterFactory2
;
42 import org
.opengis
.filter
.identity
.FeatureId
;
43 import org
.opengis
.referencing
.FactoryException
;
44 import org
.opengis
.referencing
.NoSuchAuthorityCodeException
;
45 import org
.opengis
.referencing
.crs
.CoordinateReferenceSystem
;
47 import com
.vividsolutions
.jts
.geom
.Geometry
;
48 import com
.vividsolutions
.jts
.geom
.Point
;
49 import com
.vividsolutions
.jts
.geom
.Polygon
;
51 /** Index JCR nodes containing or referencing GIS data. */
52 public class GeoJcrIndex
implements EventListener
, GisNames
, GisTypes
{
54 final static String DEFAULT_GEOM_NAME
= "the_geom";
56 private final static Log log
= LogFactory
.getLog(GeoJcrIndex
.class);
58 private DataStore dataStore
;
59 private Session session
;
60 private Executor systemExecutionService
;
62 private String crs
= "EPSG:4326";
64 /** The key is the workspace */
65 private Map
<String
, FeatureStore
<SimpleFeatureType
, SimpleFeature
>> geoJcrIndexes
= Collections
66 .synchronizedMap(new HashMap
<String
, FeatureStore
<SimpleFeatureType
, SimpleFeature
>>());
68 // TODO: use common factory finder?
69 private FilterFactory2 ff
= new FilterFactoryImpl();
71 /** Expects to execute with system authentication */
73 if (systemExecutionService
!= null)// legacy
74 systemExecutionService
.execute(new Runnable() {
83 protected void initGeoJcrIndex() {
86 SimpleFeatureType indexType
= getWorkspaceGeoJcrIndexType(session
87 .getWorkspace().getName());
88 GeoToolsUtils
.createSchemaIfNeeded(dataStore
, indexType
);
90 // register JCR listeners
91 ObservationManager om
= session
.getWorkspace()
92 .getObservationManager();
93 om
.addEventListener(this, Event
.NODE_ADDED
| Event
.NODE_REMOVED
,
94 "/", true, null, null, false);
96 // FIXME: use a different listener for properties since it resets
98 // om.addEventListener(this, Event.PROPERTY_CHANGED, "/",
99 // true, null, new String[] { GIS_LOCATED }, false);
101 } catch (RepositoryException e
) {
102 throw new ArgeoException("Cannot initialize GeoJcr index", e
);
106 public void dispose() {
109 public void onEvent(final EventIterator events
) {
110 final FeatureCollection
<SimpleFeatureType
, SimpleFeature
> toAdd
= FeatureCollections
112 final Set
<FeatureId
> toRemove
= new HashSet
<FeatureId
>();
114 // execute with system authentication so that JCR can be read
115 systemExecutionService
.execute(new Runnable() {
117 while (events
.hasNext()) {
118 Event event
= events
.nextEvent();
120 Integer eventType
= event
.getType();
121 if (Event
.NODE_ADDED
== eventType
) {
122 Node node
= session
.getNodeByIdentifier(event
124 if (node
.isNodeType(GIS_INDEXED
)) {
125 SimpleFeature feature
= mapNodeToFeature(node
,
126 getGeoJcrIndex().getSchema());
129 } else if (Event
.NODE_REMOVED
== eventType
) {
130 // we have no way to check whether the node was
132 // geoindexed without querying the index, this is
134 // more optimal to create a filter with all ideas
137 String id
= event
.getIdentifier();
138 toRemove
.add(ff
.featureId(id
));
139 } else if (Event
.PROPERTY_CHANGED
== eventType
) {
140 // TODO: monitor changes to SRS, BBOX AND CENTROID
142 } catch (Exception e
) {
143 log
.error("Cannot process event " + event
, e
);
149 // TODO: this may be more optimal to persist in one single
151 // but we will loose modifications on all nodes if a single one
154 Transaction transaction
= new DefaultTransaction();
155 getGeoJcrIndex().setTransaction(transaction
);
158 getGeoJcrIndex().addFeatures(toAdd
);
159 if (toRemove
.size() != 0)
160 getGeoJcrIndex().removeFeatures(ff
.id(toRemove
));
161 transaction
.commit();
162 } catch (Exception e
) {
163 transaction
.rollback();
164 throw new ArgeoException("Cannot persist changes", e
);
168 } catch (ArgeoException e
) {
170 } catch (IOException e
) {
171 throw new ArgeoException("Unexpected issue with the transaction", e
);
175 protected FeatureStore
<SimpleFeatureType
, SimpleFeature
> getGeoJcrIndex() {
176 String workspaceName
= session
.getWorkspace().getName();
177 if (!geoJcrIndexes
.containsKey(workspaceName
)) {
178 SimpleFeatureType indexType
= getWorkspaceGeoJcrIndexType(workspaceName
);
179 FeatureStore
<SimpleFeatureType
, SimpleFeature
> geoIndex
= GeoToolsUtils
180 .getFeatureStore(dataStore
, indexType
.getName());
181 geoJcrIndexes
.put(workspaceName
, geoIndex
);
183 return geoJcrIndexes
.get(workspaceName
);
186 protected SimpleFeatureType
getWorkspaceGeoJcrIndexType(String workspaceName
) {
188 SimpleFeatureTypeBuilder builder
= new SimpleFeatureTypeBuilder();
189 builder
.setNamespaceURI(GIS_NAMESPACE
);
190 builder
.setName(workspaceName
+ "_geojcr_index");
192 builder
.setCRS(CRS
.decode(crs
));
193 } catch (Exception e
) {
194 throw new ArgeoException("Cannot set CRS " + crs
, e
);
197 builder
.setDefaultGeometry(JcrUtils
.normalize(GIS_BBOX
));
198 builder
.add(JcrUtils
.normalize(GIS_BBOX
), Polygon
.class);
199 builder
.add(JcrUtils
.normalize(GIS_CENTROID
), Point
.class);
201 builder
.add(JcrUtils
.normalize("jcr:uuid"), String
.class);
202 builder
.add(JcrUtils
.normalize("jcr:path"), String
.class);
203 builder
.add(JcrUtils
.normalize("jcr:primaryType"), String
.class);
205 builder
.add(JcrUtils
.normalize("jcr:lastModified"), Date
.class);
206 builder
.add(JcrUtils
.normalize("jcr:lastModifiedBy"), String
.class);
208 return builder
.buildFeatureType();
211 protected SimpleFeature
mapNodeToFeature(Node node
, SimpleFeatureType type
) {
213 SimpleFeatureBuilder builder
= new SimpleFeatureBuilder(type
);
216 if (node
.isNodeType(GIS_LOCATED
)) {
218 } else if (node
.isNodeType(GIS_INDEXED
)) {
219 locatedNode
= findLocatedParent(node
);
221 throw new ArgeoException("Unsupported node " + node
);
224 // TODO: reproject to the feature store SRS
225 Polygon bbox
= (Polygon
) JtsJcrUtils
.readWkb(locatedNode
226 .getProperty(GIS_BBOX
));
227 builder
.set(JcrUtils
.normalize(GIS_BBOX
), bbox
);
228 Point centroid
= (Point
) JtsJcrUtils
.readWkb(locatedNode
229 .getProperty(GIS_CENTROID
));
230 builder
.set(JcrUtils
.normalize(GIS_CENTROID
), centroid
);
232 builder
.set(JcrUtils
.normalize("jcr:uuid"), node
.getIdentifier());
233 builder
.set(JcrUtils
.normalize("jcr:path"), node
.getPath());
234 builder
.set(JcrUtils
.normalize("jcr:primaryType"), node
235 .getPrimaryNodeType().getName());
236 if (node
.hasProperty(Property
.JCR_LAST_MODIFIED
))
237 builder
.set(JcrUtils
.normalize("jcr:lastModified"), node
238 .getProperty(Property
.JCR_LAST_MODIFIED
).getDate()
240 if (node
.hasProperty(Property
.JCR_LAST_MODIFIED_BY
))
241 builder
.set(JcrUtils
.normalize("jcr:lastModifiedBy"), node
242 .getProperty(Property
.JCR_LAST_MODIFIED_BY
).getString());
243 return builder
.buildFeature(node
.getIdentifier());
244 } catch (RepositoryException e
) {
245 throw new ArgeoException("Cannot map " + node
+ " to " + type
, e
);
249 protected Node
findLocatedParent(Node child
) {
251 if (child
.getParent().isNodeType(GIS_LOCATED
))
252 return child
.getParent();
254 return findLocatedParent(child
.getParent());
255 } catch (Exception e
) {
256 // also if child is root node
257 throw new ArgeoException("Cannot find located parent", e
);
261 /** Returns the node as a point in the CRS of the related feature store. */
262 protected Geometry
reproject(CoordinateReferenceSystem crs
,
263 Geometry geometry
, CoordinateReferenceSystem targetCrs
) {
264 // transform if not same CRS
265 // FIXME: there is certainly a more standard way to reproject
266 if (!targetCrs
.getIdentifiers().contains(crs
.getName())) {
267 throw new ArgeoException("Reprojection not yet supported");
268 // MathTransform transform;
270 // transform = CRS.findMathTransform(nodeCrs, featureStoreCrs);
271 // if (geometry instanceof Point) {
272 // Point point = (Point) geometry;
273 // DirectPosition2D pos = new DirectPosition2D(nodeCrs,
274 // point.getX(), point.getY());
275 // DirectPosition targetPos = transform.transform(pos, null);
276 // return geometryFactory.createPoint(new Coordinate(targetPos
277 // .getCoordinate()[0], targetPos.getCoordinate()[1]));
278 // } else if (geometry instanceof Polygon) {
279 // Polygon polygon = (Polygon) geometry;
280 // List<Coordinate> coordinates = new ArrayList<Coordinate>();
281 // for (Coordinate coo : polygon.getExteriorRing()) {
282 // DirectPosition pos = new DirectPosition2D(nodeCrs,
284 // DirectPosition targetPos = transform.transform(pos,
286 // // coordinates.add(o)
288 // LinearRing ring = geometryFactory
289 // .createLinearRing(coordinates
290 // .toArray(new Coordinate[coordinates.size()]));
291 // return geometryFactory.createPolygon(ring, null);
293 // } catch (Exception e) {
294 // throw new ArgeoException("Cannot transform from " + nodeCrs
295 // + " to " + featureStoreCrs, e);
302 public void setDataStore(DataStore dataStore
) {
303 this.dataStore
= dataStore
;
306 public void setSession(Session session
) {
307 this.session
= session
;
310 public void setSystemExecutionService(Executor systemExecutionService
) {
311 this.systemExecutionService
= systemExecutionService
;