1 package org
.argeo
.geotools
.jcr
;
3 import java
.io
.IOException
;
5 import java
.util
.HashSet
;
9 import javax
.jcr
.Property
;
10 import javax
.jcr
.RepositoryException
;
11 import javax
.jcr
.Session
;
12 import javax
.jcr
.observation
.Event
;
13 import javax
.jcr
.observation
.EventIterator
;
14 import javax
.jcr
.observation
.EventListener
;
16 import org
.apache
.commons
.logging
.Log
;
17 import org
.apache
.commons
.logging
.LogFactory
;
18 import org
.argeo
.ArgeoException
;
19 import org
.argeo
.geotools
.GeoToolsUtils
;
20 import org
.argeo
.jcr
.JcrUtils
;
21 import org
.argeo
.jcr
.gis
.GisNames
;
22 import org
.argeo
.jcr
.gis
.GisTypes
;
23 import org
.argeo
.jts
.jcr
.JtsJcrUtils
;
24 import org
.argeo
.security
.SystemExecutionService
;
25 import org
.geotools
.data
.DataStore
;
26 import org
.geotools
.data
.DefaultTransaction
;
27 import org
.geotools
.data
.FeatureStore
;
28 import org
.geotools
.data
.Transaction
;
29 import org
.geotools
.feature
.FeatureCollection
;
30 import org
.geotools
.feature
.FeatureCollections
;
31 import org
.geotools
.feature
.simple
.SimpleFeatureBuilder
;
32 import org
.geotools
.feature
.simple
.SimpleFeatureTypeBuilder
;
33 import org
.geotools
.filter
.FilterFactoryImpl
;
34 import org
.opengis
.feature
.simple
.SimpleFeature
;
35 import org
.opengis
.feature
.simple
.SimpleFeatureType
;
36 import org
.opengis
.filter
.FilterFactory2
;
37 import org
.opengis
.filter
.identity
.FeatureId
;
38 import org
.opengis
.referencing
.crs
.CoordinateReferenceSystem
;
40 import com
.vividsolutions
.jts
.geom
.Geometry
;
41 import com
.vividsolutions
.jts
.geom
.Point
;
42 import com
.vividsolutions
.jts
.geom
.Polygon
;
44 public class GeoJcrIndex
implements EventListener
{
45 final static String GEOJCR_INDEX
= "GEOJCR_INDEX";
47 final static String DEFAULT_GEOM_NAME
= "the_geom";
49 private final static Log log
= LogFactory
.getLog(GeoJcrIndex
.class);
51 public static SimpleFeatureType
getWorkspaceGeoIndex(String workspaceName
) {
52 SimpleFeatureTypeBuilder builder
= new SimpleFeatureTypeBuilder();
53 builder
.setNamespaceURI(GisNames
.GIS_NAMESPACE
);
54 builder
.setName(workspaceName
.toUpperCase() + "_" + GEOJCR_INDEX
);
56 builder
.setDefaultGeometry(JcrUtils
.normalize(GisNames
.GIS_BBOX
));
57 builder
.add(JcrUtils
.normalize(GisNames
.GIS_BBOX
), Polygon
.class);
58 builder
.add(JcrUtils
.normalize(GisNames
.GIS_CENTROID
), Point
.class);
60 builder
.add(JcrUtils
.normalize(Property
.JCR_UUID
), String
.class);
61 builder
.add(JcrUtils
.normalize(Property
.JCR_PATH
), String
.class);
62 builder
.add(JcrUtils
.normalize(Property
.JCR_PRIMARY_TYPE
), String
.class);
64 builder
.add(JcrUtils
.normalize(Property
.JCR_LAST_MODIFIED
), Date
.class);
65 builder
.add(JcrUtils
.normalize(Property
.JCR_LAST_MODIFIED_BY
),
68 return builder
.buildFeatureType();
71 private DataStore dataStore
;
72 private Session session
;
73 private SystemExecutionService systemExecutionService
;
75 // TODO: use common factory finder?
76 private FilterFactory2 ff
= new FilterFactoryImpl();
80 systemExecutionService
.executeAsSystem(new Runnable() {
83 session
.getWorkspace()
84 .getObservationManager()
85 .addEventListener(GeoJcrIndex
.this,
86 Event
.NODE_ADDED
| Event
.NODE_REMOVED
, "/",
88 new String
[] { GisTypes
.GIS_INDEXED
},
90 } catch (RepositoryException e
) {
91 throw new ArgeoException("Cannot initialize GeoJcr index",
99 public void dispose() {
102 public void onEvent(EventIterator events
) {
103 SimpleFeatureType indexType
= getWorkspaceGeoIndex(session
104 .getWorkspace().getName());
105 GeoToolsUtils
.createSchemaIfNeeded(dataStore
, indexType
);
106 FeatureStore
<SimpleFeatureType
, SimpleFeature
> geoJcrIndex
= GeoToolsUtils
107 .getFeatureStore(dataStore
, indexType
.getName());
109 FeatureCollection
<SimpleFeatureType
, SimpleFeature
> toAdd
= FeatureCollections
111 Set
<FeatureId
> toRemove
= new HashSet
<FeatureId
>();
112 while (events
.hasNext()) {
113 Event event
= events
.nextEvent();
115 Integer eventType
= event
.getType();
116 if (Event
.NODE_ADDED
== eventType
) {
117 Node node
= session
.getNodeByIdentifier(event
119 if (node
.isNodeType(GisTypes
.GIS_LOCATED
)) {
120 SimpleFeature feature
= mapNodeToFeature(node
,
124 } else if (Event
.NODE_REMOVED
== eventType
) {
125 String id
= event
.getIdentifier();
126 toRemove
.add(ff
.featureId(id
));
128 } catch (Exception e
) {
129 log
.error("Cannot process event " + event
, e
);
134 // TODO: this may be more optimal to persist in one single transaction,
135 // but we will loose modifications on all nodes if one fails
137 Transaction transaction
= new DefaultTransaction();
138 geoJcrIndex
.setTransaction(transaction
);
141 geoJcrIndex
.addFeatures(toAdd
);
142 if (toRemove
.size() != 0)
143 geoJcrIndex
.removeFeatures(ff
.id(toRemove
));
144 transaction
.commit();
145 } catch (Exception e
) {
146 transaction
.rollback();
147 throw new ArgeoException("Cannot persist changes", e
);
151 } catch (ArgeoException e
) {
153 } catch (IOException e
) {
154 throw new ArgeoException("Unexpected issue with the transaction", e
);
158 protected SimpleFeature
mapNodeToFeature(Node node
, SimpleFeatureType type
) {
160 SimpleFeatureBuilder builder
= new SimpleFeatureBuilder(type
);
163 if (node
.isNodeType(GisTypes
.GIS_LOCATED
)) {
165 } else if (node
.isNodeType(GisTypes
.GIS_INDEXED
)) {
166 locatedNode
= findLocatedparent(node
);
168 throw new ArgeoException("Unsupported node " + node
);
171 // TODO: reproject to the feature store SRS
172 Polygon bbox
= (Polygon
) JtsJcrUtils
.readWkb(locatedNode
173 .getProperty(GisNames
.GIS_BBOX
));
174 builder
.set(JcrUtils
.normalize(GisNames
.GIS_BBOX
), bbox
);
175 Polygon centroid
= (Polygon
) JtsJcrUtils
.readWkb(locatedNode
176 .getProperty(GisNames
.GIS_CENTROID
));
177 builder
.set(JcrUtils
.normalize(GisNames
.GIS_CENTROID
), centroid
);
179 builder
.set(JcrUtils
.normalize(Property
.JCR_UUID
),
180 node
.getIdentifier());
181 builder
.set(JcrUtils
.normalize(Property
.JCR_PATH
), node
.getPath());
182 builder
.set(JcrUtils
.normalize(Property
.JCR_PRIMARY_TYPE
), node
183 .getPrimaryNodeType().getName());
184 if (node
.hasProperty(Property
.JCR_LAST_MODIFIED
))
185 builder
.set(JcrUtils
.normalize(Property
.JCR_LAST_MODIFIED
),
186 node
.getProperty(Property
.JCR_LAST_MODIFIED
).getDate()
188 if (node
.hasProperty(Property
.JCR_LAST_MODIFIED_BY
))
189 builder
.set(JcrUtils
.normalize(Property
.JCR_LAST_MODIFIED_BY
),
190 node
.getProperty(Property
.JCR_LAST_MODIFIED_BY
)
192 return builder
.buildFeature(node
.getIdentifier());
193 } catch (RepositoryException e
) {
194 throw new ArgeoException("Cannot map " + node
+ " to " + type
, e
);
198 protected Node
findLocatedparent(Node child
) {
200 if (child
.getParent().isNodeType(GisTypes
.GIS_LOCATED
))
201 return child
.getParent();
203 return findLocatedparent(child
.getParent());
204 } catch (Exception e
) {
205 // also if child is root node
206 throw new ArgeoException("Cannot find located parent", e
);
210 /** Returns the node as a point in the CRS of the related feature store. */
211 protected Geometry
reproject(CoordinateReferenceSystem crs
,
212 Geometry geometry
, CoordinateReferenceSystem targetCrs
) {
213 // transform if not same CRS
214 // FIXME: there is certainly a more standard way to reproject
215 if (!targetCrs
.getIdentifiers().contains(crs
.getName())) {
216 throw new ArgeoException("Reprojection not yet supported");
217 // MathTransform transform;
219 // transform = CRS.findMathTransform(nodeCrs, featureStoreCrs);
220 // if (geometry instanceof Point) {
221 // Point point = (Point) geometry;
222 // DirectPosition2D pos = new DirectPosition2D(nodeCrs,
223 // point.getX(), point.getY());
224 // DirectPosition targetPos = transform.transform(pos, null);
225 // return geometryFactory.createPoint(new Coordinate(targetPos
226 // .getCoordinate()[0], targetPos.getCoordinate()[1]));
227 // } else if (geometry instanceof Polygon) {
228 // Polygon polygon = (Polygon) geometry;
229 // List<Coordinate> coordinates = new ArrayList<Coordinate>();
230 // for (Coordinate coo : polygon.getExteriorRing()) {
231 // DirectPosition pos = new DirectPosition2D(nodeCrs,
233 // DirectPosition targetPos = transform.transform(pos,
235 // // coordinates.add(o)
237 // LinearRing ring = geometryFactory
238 // .createLinearRing(coordinates
239 // .toArray(new Coordinate[coordinates.size()]));
240 // return geometryFactory.createPolygon(ring, null);
242 // } catch (Exception e) {
243 // throw new ArgeoException("Cannot transform from " + nodeCrs
244 // + " to " + featureStoreCrs, e);
251 public void setDataStore(DataStore dataStore
) {
252 this.dataStore
= dataStore
;
255 public void setSession(Session session
) {
256 this.session
= session
;
259 public void setSystemExecutionService(
260 SystemExecutionService systemExecutionService
) {
261 this.systemExecutionService
= systemExecutionService
;