1 package nl.geozet.wfs;
2
3 import static nl.geozet.common.NumberConstants.DEFAULT_MAX_FEATURES;
4 import static nl.geozet.common.NumberConstants.OPENLS_ZOOMSCALE_STANDAARD;
5 import static nl.geozet.common.StringConstants.AFSTAND_NAAM;
6 import static nl.geozet.common.StringConstants.CONFIG_PARAM_WFS_CAPABILITIES_URL;
7 import static nl.geozet.common.StringConstants.CONFIG_PARAM_WFS_TYPENAME;
8 import static nl.geozet.common.StringConstants.FEATURE_ATTR_NAAM_CATEGORIE;
9 import static nl.geozet.common.StringConstants.FEATURE_ATTR_NAAM_ONDERWERP;
10 import static nl.geozet.common.StringConstants.FEATURE_ATTR_NAAM_STRAAT;
11 import static nl.geozet.common.StringConstants.FEATURE_ATTR_NAAM_TITEL;
12 import static nl.geozet.common.StringConstants.FILTER_CATEGORIE_NAAM;
13 import static nl.geozet.common.StringConstants.REQ_PARAM_EXPLICITUSEFILTER;
14 import static nl.geozet.common.StringConstants.REQ_PARAM_FID;
15 import static nl.geozet.common.StringConstants.REQ_PARAM_FILTER;
16 import static nl.geozet.common.StringConstants.REQ_PARAM_GEVONDEN;
17 import static nl.geozet.common.StringConstants.REQ_PARAM_PAGEOFFSET;
18 import static nl.geozet.common.StringConstants.REQ_PARAM_STRAAL;
19 import static nl.geozet.common.StringConstants.REQ_PARAM_XCOORD;
20 import static nl.geozet.common.StringConstants.REQ_PARAM_YCOORD;
21 import static nl.geozet.common.StringConstants.SERVLETCONFIG_WFS_MAXFEATURES;
22 import static nl.geozet.common.StringConstants.SERVLETCONFIG_WFS_TIMEOUT;
23
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.text.DecimalFormat;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Vector;
31
32 import javax.servlet.RequestDispatcher;
33 import javax.servlet.ServletConfig;
34 import javax.servlet.ServletException;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import nl.geozet.common.AfstandComparator;
39 import nl.geozet.common.ServletBase;
40
41 import org.apache.log4j.Logger;
42 import org.geotools.data.DataStore;
43 import org.geotools.data.DataStoreFinder;
44 import org.geotools.data.Query;
45 import org.geotools.data.simple.SimpleFeatureCollection;
46 import org.geotools.data.simple.SimpleFeatureIterator;
47 import org.geotools.data.simple.SimpleFeatureSource;
48 import org.geotools.data.wfs.WFSDataStoreFactory;
49 import org.geotools.factory.Hints;
50 import org.geotools.filter.text.cql2.CQL;
51 import org.geotools.filter.text.cql2.CQLException;
52 import org.geotools.geometry.jts.JTSFactoryFinder;
53 import org.geotools.referencing.CRS;
54 import org.opengis.feature.simple.SimpleFeature;
55 import org.opengis.feature.simple.SimpleFeatureType;
56 import org.opengis.filter.Filter;
57 import org.opengis.referencing.FactoryException;
58 import org.opengis.referencing.NoSuchAuthorityCodeException;
59
60 import com.vividsolutions.jts.geom.Coordinate;
61 import com.vividsolutions.jts.geom.Geometry;
62 import com.vividsolutions.jts.geom.GeometryFactory;
63 import com.vividsolutions.jts.geom.Point;
64
65
66
67
68
69
70
71
72
73
74 public class WFSClientServlet extends ServletBase {
75
76
77 private static final long serialVersionUID = -1293974305859874046L;
78
79
80 private static final Logger LOGGER = Logger
81 .getLogger(WFSClientServlet.class);
82
83
84
85
86 private transient DataStore data = null;
87
88
89
90
91 private final Map<String, Object> connectionParameters = new HashMap<String, Object>();
92
93 private GeometryFactory geometryFactory;
94
95
96
97
98 protected transient SimpleFeatureType schema;
99
100
101
102
103 protected transient SimpleFeatureSource source;
104
105
106 protected String typeName;
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 @Override
122 protected void service(HttpServletRequest request,
123 HttpServletResponse response) throws ServletException, IOException {
124
125 double xcoord;
126 double ycoord;
127 double straal;
128 try {
129
130 xcoord = Double
131 .valueOf(request.getParameter(REQ_PARAM_XCOORD.code));
132 ycoord = Double
133 .valueOf(request.getParameter(REQ_PARAM_YCOORD.code));
134 straal = Double
135 .valueOf((null == request
136 .getParameter(REQ_PARAM_STRAAL.code) ? OPENLS_ZOOMSCALE_STANDAARD
137 .toString() : request
138 .getParameter(REQ_PARAM_STRAAL.code)));
139 } catch (final NullPointerException e) {
140 LOGGER.error(
141 "Een van de vereiste parameters werd niet in het request gevonden.",
142 e);
143
144
145
146
147
148
149
150 throw new ServletException(
151 "Een van de vereiste parameters werd niet in het request gevonden.",
152 e);
153 } catch (final NumberFormatException e) {
154 LOGGER.error(
155 "Een van de vereiste parameters kon niet geparsed worden als Double.",
156 e);
157
158
159
160
161
162
163
164 throw new ServletException(
165 "Een van de vereiste parameters kon niet geparsed worden als Double.",
166 e);
167 }
168 LOGGER.debug("request params:" + xcoord + ":" + ycoord + " straal:"
169 + straal);
170
171
172 final String[] fString = request
173 .getParameterValues(REQ_PARAM_FILTER.code);
174 final String filterUsed = request
175 .getParameter(REQ_PARAM_EXPLICITUSEFILTER.code);
176 if ((null == fString) && (null != filterUsed)
177 && filterUsed.equalsIgnoreCase("true")) {
178 LOGGER.debug("Er is expres gezocht met een leeg filter");
179
180
181
182 this.renderHTMLResults(request, response,
183 new Vector<SimpleFeature>());
184 } else {
185 LOGGER.debug("Er is wordt gezocht met een filter");
186
187 final Filter filter = this.maakFilter(xcoord, ycoord, straal,
188 fString);
189
190 final Vector<SimpleFeature> results = this.ophalenBekendmakingen(
191 filter, xcoord, ycoord);
192
193 this.renderHTMLResults(request, response, results);
194 }
195 response.flushBuffer();
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 @Override
212 public void init(ServletConfig config) throws ServletException {
213 LOGGER.debug("opstarten servlet");
214 super.init(config);
215
216 final String capsUrl = this.getServletContext().getInitParameter(
217 CONFIG_PARAM_WFS_CAPABILITIES_URL.code);
218 if (capsUrl == null) {
219 LOGGER.fatal("config param " + CONFIG_PARAM_WFS_CAPABILITIES_URL
220 + " is null.");
221 throw new ServletException("config param "
222 + CONFIG_PARAM_WFS_CAPABILITIES_URL + " is null.");
223 }
224 this.connectionParameters.put(
225 "WFSDataStoreFactory:GET_CAPABILITIES_URL", capsUrl);
226 this.typeName = config.getInitParameter(CONFIG_PARAM_WFS_TYPENAME.code);
227 if (this.typeName == null) {
228 LOGGER.fatal("Config param " + CONFIG_PARAM_WFS_TYPENAME
229 + " is null.");
230 throw new ServletException("Config param "
231 + CONFIG_PARAM_WFS_TYPENAME + " is null.");
232 }
233 LOGGER.debug("typeName is: " + this.typeName);
234
235 final String wfsTimeout = config
236 .getInitParameter(SERVLETCONFIG_WFS_TIMEOUT.code);
237 int timeout;
238 try {
239 timeout = Integer.valueOf(wfsTimeout);
240 } catch (final Exception e) {
241 timeout = 5;
242 }
243 LOGGER.info("WFS timeout voor servlet: " + this.getServletName()
244 + " ingesteld op: " + timeout + " sec.");
245 this.connectionParameters.put(WFSDataStoreFactory.TIMEOUT.key,
246 (timeout * 1000));
247
248 final String wfsMaxFeat = config
249 .getInitParameter(SERVLETCONFIG_WFS_MAXFEATURES.code);
250 int maxFeat;
251 try {
252 maxFeat = Integer.valueOf(wfsMaxFeat);
253 } catch (final Exception e) {
254 maxFeat = DEFAULT_MAX_FEATURES.intValue();
255 }
256 LOGGER.info("WFS max. features voor servlet: " + this.getServletName()
257 + " ingesteld op: " + maxFeat + "");
258 this.connectionParameters.put(WFSDataStoreFactory.MAXFEATURES.key,
259 maxFeat);
260
261 this.connectionParameters.put(WFSDataStoreFactory.BUFFER_SIZE.key, 20);
262
263
264
265
266
267 try {
268 this.data = DataStoreFinder.getDataStore(this.connectionParameters);
269 this.schema = this.data.getSchema(this.typeName);
270 LOGGER.debug("Schema Attributen: "
271 + this.schema.getAttributeCount());
272 this.source = this.data.getFeatureSource(this.typeName);
273 LOGGER.debug("Metadata bounds: " + this.source.getBounds());
274
275 final Hints hints = new Hints(Hints.CRS, CRS.decode("EPSG:28992"));
276 this.geometryFactory = JTSFactoryFinder.getGeometryFactory(hints);
277 } catch (final IOException e) {
278 LOGGER.fatal(
279 "Verbinding met de WFS is mislukt. Controleer de configuratie en herstart de applicatie.",
280 e);
281 throw new ServletException(
282 "Verbinding met de WFS server is mislukt.", e);
283 } catch (final NoSuchAuthorityCodeException e) {
284 LOGGER.fatal("De gevraagde CRS autoriteit is niet gevonden.", e);
285 throw new ServletException(
286 "De gevraagde CRS autoriteit is niet gevonden.", e);
287 } catch (final FactoryException e) {
288 LOGGER.fatal(
289 "Gevraagde GeoTools factory voor CRS is niet gevonden.", e);
290 throw new ServletException(
291 "Gevraagde GeoTools factory voor CRS is niet gevonden.", e);
292 }
293 LOGGER.debug("schema info: " + this.schema);
294 }
295
296
297
298
299
300
301 @Override
302 public void destroy() {
303 this.data.dispose();
304 this.data = null;
305 this.schema = null;
306 this.source = null;
307 this.geometryFactory = null;
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327 private Filter maakFilter(double xcoord, double ycoord, double straal,
328 String[] categorieen) throws ServletException {
329
330 Filter filter;
331 final StringBuilder filterString = new StringBuilder();
332
333 filterString.append("DWITHIN("
334 + this.schema.getGeometryDescriptor().getLocalName()
335 + ", POINT(" + xcoord + " " + ycoord + "), " + straal
336
337
338
339
340 + ", meters)");
341
342 if (categorieen != null) {
343 filterString.append(" AND (");
344 for (int i = 0; i < categorieen.length; i++) {
345 filterString.append(FILTER_CATEGORIE_NAAM + "='");
346 filterString.append(categorieen[i]);
347 filterString.append("'");
348 if (i < categorieen.length - 1) {
349 filterString.append(" OR ");
350 }
351 }
352 filterString.append(")");
353 }
354 LOGGER.debug("CQL voor filter is: " + filterString);
355
356 try {
357 filter = CQL.toFilter(filterString.toString());
358 } catch (final CQLException e) {
359 LOGGER.error("CQL Fout in de query voor de WFS.", e);
360 throw new ServletException("CQL Fout in de query voor de WFS.", e);
361 }
362 return filter;
363 }
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381 protected Vector<SimpleFeature> ophalenBekendmakingen(Filter filter,
382 double xcoord, double ycoord) throws ServletException, IOException {
383
384 final Query query = new Query();
385 try {
386 query.setCoordinateSystem(CRS.decode("EPSG:28992"));
387 query.setTypeName(this.typeName);
388 query.setFilter(filter);
389 query.setPropertyNames(Query.ALL_NAMES);
390 query.setHandle("GEOZET-handle");
391 } catch (final NoSuchAuthorityCodeException e) {
392 LOGGER.fatal("De gevraagde CRS autoriteit is niet gevonden.", e);
393 throw new ServletException(
394 "De gevraagde CRS autoriteit is niet gevonden.", e);
395 } catch (final FactoryException e) {
396 LOGGER.fatal(
397 "Gevraagde GeoTools factory voor CRS is niet gevonden.", e);
398 throw new ServletException(
399 "Gevraagde GeoTools factory voor CRS is niet gevonden.", e);
400 }
401
402
403 final SimpleFeatureCollection features = this.source.getFeatures(query);
404 LOGGER.debug("Er zijn " + features.size() + " features opgehaald.");
405
406
407 final Point p = this.geometryFactory.createPoint(new Coordinate(xcoord,
408 ycoord));
409
410 double afstand = -1d;
411 final Vector<SimpleFeature> bekendmakingen = new Vector<SimpleFeature>();
412
413 final SimpleFeatureIterator iterator = features.features();
414 try {
415 while (iterator.hasNext()) {
416
417
418 final SimpleFeature feature = iterator.next();
419 LOGGER.debug("Opgehaalde feature: " + feature);
420 afstand = p.distance((Geometry) feature
421 .getDefaultGeometryProperty().getValue());
422 feature.getUserData().put(AFSTAND_NAAM, afstand);
423 bekendmakingen.add(feature);
424 }
425 } finally {
426 iterator.close();
427 }
428
429 Collections.sort(bekendmakingen, new AfstandComparator());
430 LOGGER.debug("Er zijn " + bekendmakingen.size()
431 + " features gesorteerd.");
432 return bekendmakingen;
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450 protected void renderHTMLResults(HttpServletRequest request,
451 HttpServletResponse response, Vector<SimpleFeature> results)
452 throws IOException, ServletException {
453
454 response.setContentType("text/html; charset=UTF-8");
455 response.setBufferSize(8192);
456
457
458 final RequestDispatcher header = this.getServletContext()
459 .getRequestDispatcher("/WEB-INF/zoekresultaat_begin.jsp");
460 if (header != null) {
461 header.include(request, response);
462 }
463
464 final StringBuilder sb = new StringBuilder();
465 sb.append(this._RESOURCES.getString("KEY_BEKENDMAKINGEN_TITEL"));
466 sb.append("<dl class=\"geozetResults\">");
467
468 sb.append("<dt class=\"first\">")
469 .append(this._RESOURCES
470 .getString("KEY_BEKENDMAKINGEN_GEVONDEN"))
471 .append("</dt>");
472
473 sb.append("<dd>")
474 .append(results.size())
475 .append(" ")
476 .append(this._RESOURCES
477 .getString("KEY_BEKENDMAKINGEN_RESULTATEN"))
478 .append("</dd>");
479
480 sb.append("<dt>")
481 .append(this._RESOURCES.getString("KEY_BEKENDMAKINGEN_GEZOCHT"))
482 .append(" ").append("</dt>");
483 sb.append("<dd>").append(request.getParameter(REQ_PARAM_GEVONDEN.code))
484 .append("</dd>");
485 sb.append("</dl>");
486
487 if (results.size() < 1) {
488 sb.append("<p>")
489 .append(this._RESOURCES
490 .getString("KEY_BEKENDMAKINGEN_NIETSGEVONDEN"))
491 .append("</p>\n");
492 }
493
494 final String paginering = this.buildPageList(results.size(), request);
495 sb.append(paginering);
496 if (results.size() > 0) {
497
498 sb.append("<ol id=\"geozetResultsList\">");
499 }
500
501 final DecimalFormat fmtKilometer = new DecimalFormat("#.#");
502 final DecimalFormat fmtMeter = new DecimalFormat("#");
503
504
505 final String qString = this.buildQueryString(request, "");
506
507
508
509 int currentOffset;
510 try {
511 currentOffset = Integer.parseInt(request
512 .getParameter(REQ_PARAM_PAGEOFFSET.code));
513 } catch (final NumberFormatException e) {
514 currentOffset = 0;
515 }
516 final int renderItems = (currentOffset + this.itemsPerPage > results
517 .size() ? results.size() : currentOffset + this.itemsPerPage);
518
519 for (int index = currentOffset; index < renderItems; index++) {
520 final SimpleFeature f = results.get(index);
521 sb.append("<li>");
522 sb.append("<a href=\"").append(this._BEKENDMAKINGDETAIL)
523 .append("?").append(REQ_PARAM_FID).append("=")
524 .append(f.getID()).append(qString).append("\">");
525 sb.append("<strong>")
526 .append(this.featureAttribuutCheck(f
527 .getAttribute(FEATURE_ATTR_NAAM_TITEL.code)))
528 .append("</strong>");
529 sb.append("</a>");
530 sb.append(this.featureAttribuutCheck(f
531 .getAttribute(FEATURE_ATTR_NAAM_ONDERWERP.code)));
532 sb.append(" (")
533 .append(this.featureAttribuutCheck(f
534 .getAttribute(FEATURE_ATTR_NAAM_CATEGORIE.code)))
535 .append(")");
536
537 sb.append("<span>locatie: ").append(
538 this.featureAttribuutCheck(f
539 .getAttribute(FEATURE_ATTR_NAAM_STRAAT.code)));
540
541
542
543
544
545
546
547
548 final double afstand = Double.valueOf(f.getUserData()
549 .get(AFSTAND_NAAM).toString());
550 if (afstand >= 1000) {
551
552 sb.append(" (").append(fmtKilometer.format(afstand / 1000))
553 .append(" km)");
554 } else {
555 sb.append(" (").append(fmtMeter.format(afstand)).append(" m)");
556 }
557 sb.append("</span>");
558 sb.append("</li>");
559
560 }
561 if (results.size() > 0) {
562
563 sb.append("</ol>");
564 }
565 sb.append(paginering);
566
567
568 sb.append(this._RESOURCES.getString("KEY_BEKENDMAKINGEN_OVERIG_TITEL"));
569 sb.append("<p><a href=\"");
570 sb.append(this._BEKENDMAKINGENVLAK)
571 .append("?")
572 .append(this.buildQueryString(request,
573 REQ_PARAM_PAGEOFFSET.code));
574 sb.append("\">");
575 sb.append(this._RESOURCES.getString("KEY_BEKENDMAKINGEN_OVERIG"));
576 sb.append("</a></p>");
577
578 final PrintWriter out = response.getWriter();
579 out.print(sb);
580 out.flush();
581
582
583 final RequestDispatcher footer = this.getServletContext()
584 .getRequestDispatcher("/WEB-INF/zoekresultaat_einde.jsp");
585 if (footer != null) {
586 footer.include(request, response);
587 }
588 }
589 }