001: //$HeadURL: $
002: /*---------------- FILE HEADER ------------------------------------------
003: This file is part of deegree.
004: Copyright (C) 2001-2008 by:
005: Department of Geography, University of Bonn
006: http://www.giub.uni-bonn.de/deegree/
007: lat/lon GmbH
008: http://www.lat-lon.de
009:
010: This library is free software; you can redistribute it and/or
011: modify it under the terms of the GNU Lesser General Public
012: License as published by the Free Software Foundation; either
013: version 2.1 of the License, or (at your option) any later version.
014: This library is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018: You should have received a copy of the GNU Lesser General Public
019: License along with this library; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: Contact:
022:
023: Andreas Poth
024: lat/lon GmbH
025: Aennchenstr. 19
026: 53177 Bonn
027: Germany
028: E-Mail: poth@lat-lon.de
029:
030: Prof. Dr. Klaus Greve
031: Department of Geography
032: University of Bonn
033: Meckenheimer Allee 166
034: 53115 Bonn
035: Germany
036: E-Mail: greve@giub.uni-bonn.de
037: ---------------------------------------------------------------------------*/
038:
039: package org.deegree.ogcwebservices.wcts.operation;
040:
041: import static org.deegree.framework.xml.XMLTools.getElement;
042: import static org.deegree.framework.xml.XMLTools.getElements;
043: import static org.deegree.framework.xml.XMLTools.getNodeAsBoolean;
044: import static org.deegree.framework.xml.XMLTools.getNodeAsString;
045: import static org.deegree.framework.xml.XMLTools.getStringValue;
046: import static org.deegree.ogcbase.CommonNamespaces.DEEGREEWCTS;
047: import static org.deegree.ogcbase.CommonNamespaces.DEEGREEWCTS_PREFIX;
048: import static org.deegree.ogcbase.CommonNamespaces.GML_PREFIX;
049: import static org.deegree.ogcbase.CommonNamespaces.OWS_1_1_0PREFIX;
050: import static org.deegree.ogcbase.CommonNamespaces.WCS_1_2_0_PREFIX;
051:
052: import java.util.ArrayList;
053: import java.util.List;
054:
055: import javax.vecmath.Point3d;
056:
057: import org.deegree.framework.log.ILogger;
058: import org.deegree.framework.log.LoggerFactory;
059: import org.deegree.framework.xml.XMLParsingException;
060: import org.deegree.i18n.Messages;
061: import org.deegree.model.crs.CRSFactory;
062: import org.deegree.model.crs.CoordinateSystem;
063: import org.deegree.model.crs.UnknownCRSException;
064: import org.deegree.model.feature.FeatureCollection;
065: import org.deegree.model.feature.GMLFeatureCollectionDocument;
066: import org.deegree.model.spatialschema.GMLGeometryAdapter;
067: import org.deegree.model.spatialschema.Geometry;
068: import org.deegree.model.spatialschema.GeometryException;
069: import org.deegree.ogcbase.ExceptionCode;
070: import org.deegree.ogcwebservices.OGCWebServiceException;
071: import org.deegree.ogcwebservices.wcts.WCTSExceptionCode;
072: import org.deegree.ogcwebservices.wcts.WCTServiceFactory;
073: import org.deegree.ogcwebservices.wcts.capabilities.Content;
074: import org.deegree.ogcwebservices.wcts.capabilities.FeatureAbilities;
075: import org.deegree.ogcwebservices.wcts.capabilities.InputOutputFormat;
076: import org.deegree.ogcwebservices.wcts.configuration.WCTSConfiguration;
077: import org.deegree.ogcwebservices.wcts.data.FeatureCollectionData;
078: import org.deegree.ogcwebservices.wcts.data.GeometryData;
079: import org.deegree.ogcwebservices.wcts.data.SimpleData;
080: import org.deegree.ogcwebservices.wcts.data.TransformableData;
081: import org.deegree.owscommon_1_1_0.Manifest;
082: import org.deegree.owscommon_1_1_0.ManifestDocument;
083: import org.w3c.dom.Element;
084: import org.w3c.dom.Node;
085:
086: /**
087: * <code>WCTSTransformDocument</code> is a helper class which supplies a constructor to parse wcts Transform requests
088: * version 0.4.0.
089: * <p>
090: * Following elements are currently not supported:
091: * <ul>
092: * <li>wcts:transformation</li>
093: * <li>wcs:GridCRS</li>
094: * <li>wcts:InterpolationType, which is of type wcs:InterpolationMethodBaseType</li>
095: * </ul>
096: * </p>
097: *
098: * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
099: *
100: * @author last edited by: $Author:$
101: *
102: * @version $Revision:$, $Date:$
103: *
104: */
105: public class TransformDocument extends WCTSRequestBaseDocument {
106: private static ILogger LOG = LoggerFactory
107: .getLogger(TransformDocument.class);
108:
109: private static final long serialVersionUID = 1343985893563449983L;
110:
111: private final Transform transformRequest;
112:
113: /**
114: * @param requestId
115: * @param rootElement
116: * should not be <code>null</code>
117: * @throws OGCWebServiceException
118: * if an {@link XMLParsingException} occurred or a mandatory element/attribute is missing.
119: */
120: public TransformDocument(String requestId, Element rootElement)
121: throws OGCWebServiceException {
122: super (rootElement);
123: String version = parseVersion();
124:
125: // check for valid request.
126: parseService();
127:
128: try {
129: String sCRS = getNodeAsString(getRootElement(), PRE
130: + "SourceCRS", nsContext, null);
131: String tCRS = getNodeAsString(getRootElement(), PRE
132: + "TargetCRS", nsContext, null);
133: CoordinateSystem sourceCRS = null;
134: CoordinateSystem targetCRS = null;
135: if ((sCRS != null && tCRS == null)
136: || (sCRS == null && tCRS != null)) {
137: throw new OGCWebServiceException(Messages.getMessage(
138: "WCTS_ISTRANSFORMABLE_MISSING_CRS",
139: ((sCRS == null) ? "TargetCRS" : "SourceCRS"),
140: ((sCRS == null) ? "SourceCRS" : "TargetCRS")),
141: ExceptionCode.INVALIDPARAMETERVALUE);
142: }
143: if (sCRS != null && tCRS != null) {
144: sourceCRS = CRSFactory.create(sCRS);
145: targetCRS = CRSFactory.create(tCRS);
146: } else {
147: // Check for not supported transformation element.
148: Element transformation = getElement(getRootElement(),
149: PRE + "Transformation", nsContext);
150: if (transformation != null) {
151: throw new OGCWebServiceException(
152: Messages
153: .getMessage(
154: "WCTS_OPERATION_NOT_SUPPORTED",
155: "defining of transformations ("
156: + PRE
157: + "Transformation element)"),
158: ExceptionCode.OPERATIONNOTSUPPORTED);
159: }
160: throw new OGCWebServiceException(Messages.getMessage(
161: "WCTS_NOT_VALID_XML_CHOICE", PRE
162: + "SourceCRS/TargetCRS and " + PRE
163: + "Transformation"),
164: ExceptionCode.MISSINGPARAMETERVALUE);
165: }
166: // Check for not supported gridcrs.
167: Element gridCRS = getElement(getRootElement(),
168: WCS_1_2_0_PREFIX + ":GridCRS", nsContext);
169: if (gridCRS != null) {
170: throw new OGCWebServiceException(Messages.getMessage(
171: "WCTS_OPERATION_NOT_SUPPORTED",
172: "Definition of output GridCRS ("
173: + WCS_1_2_0_PREFIX
174: + ":GridCRS element)"),
175: ExceptionCode.OPERATIONNOTSUPPORTED);
176: }
177:
178: Element inputDataElement = getElement(getRootElement(),
179: OWS_1_1_0PREFIX + ":InputData", nsContext);
180: Manifest inputData = null;
181: TransformableData<?> transformableData = null;
182: int dataPresentation = Transform.MULTIPART;
183: if (inputDataElement != null) {
184: ManifestDocument doc = new ManifestDocument();
185: inputData = doc.parseManifestType(inputDataElement);
186: /**
187: * get deegree specific elements. The featurecollections provided by the mime/multiparts were put
188: * beneath the d_wcts:InsertedMultiparts.
189: */
190: Element multiParts = getElement(inputDataElement,
191: DEEGREEWCTS_PREFIX + ":MultiParts", nsContext);
192: if (multiParts != null) {
193: List<FeatureCollection> allData = parseFeatureCollectionData(multiParts);
194: transformableData = new FeatureCollectionData(
195: sourceCRS, targetCRS, allData);
196: }
197: } else {
198: LOG
199: .logDebug("Found no "
200: + OWS_1_1_0PREFIX
201: + ":InputData element, now checking for the deegree element");
202: inputDataElement = getElement(getRootElement(),
203: DEEGREEWCTS_PREFIX + ":InputData", nsContext);
204: if (inputDataElement != null) {
205: ManifestDocument doc = new ManifestDocument();
206: inputData = doc.parseManifestType(inputDataElement);
207: Element inlineData = getElement(inputDataElement,
208: DEEGREEWCTS_PREFIX + ":InlineData",
209: nsContext);
210: if (inlineData != null) {
211: transformableData = parseInlineData(sourceCRS,
212: targetCRS, inlineData);
213: dataPresentation = Transform.INLINE;
214: } else {
215: // Handle the xlink:href attributes of the inputdata/referencegroup/reference@xlink:href.
216: }
217: }
218: }
219: if (inputData == null || transformableData == null) {
220: throw new OGCWebServiceException(Messages
221: .getMessage("WCTS_TRANSFORM_MISSING_DATA"),
222: WCTSExceptionCode.NO_INPUT_DATA);
223: }
224:
225: // Check for not supported interpolationType.
226: Element interpolationType = getElement(getRootElement(),
227: PRE + "InterpolationType", nsContext);
228: if (interpolationType != null) {
229: throw new OGCWebServiceException(Messages.getMessage(
230: "WCTS_OPERATION_NOT_SUPPORTED",
231: "Defining an InterpolationType (" + PRE
232: + "InterpolationType element)"),
233: ExceptionCode.OPERATIONNOTSUPPORTED);
234: }
235:
236: String outputFormat = getNodeAsString(getRootElement(), PRE
237: + "OutputFormat", nsContext, null);
238: if (outputFormat != null
239: && !"".equals(outputFormat.trim())
240: && !"text/xml; gmlVersion=3.1.1"
241: .equalsIgnoreCase(outputFormat.trim())) {
242: WCTSConfiguration config = WCTServiceFactory
243: .getConfiguration();
244: boolean outputFormatDefined = false;
245: if (config != null) {
246: Content content = config.getContents();
247: if (content != null) {
248: FeatureAbilities fa = content
249: .getFeatureAbilities();
250: if (fa != null) {
251: List<InputOutputFormat> formats = fa
252: .getFeatureFormats();
253: if (formats != null) {
254: for (InputOutputFormat format : formats) {
255: if (outputFormatDefined
256: && format != null
257: && format.canOutput()) {
258: outputFormat.equals(format
259: .getValue());
260: outputFormatDefined = true;
261: }
262: }
263: }
264: }
265: }
266: }
267: if (!outputFormatDefined) {
268: throw new OGCWebServiceException(
269: Messages
270: .getMessage(
271: "WCTS_REQUESTED_OUTPUTFORMAT_NOT_KNOWN",
272: outputFormat, "Transform"),
273: WCTSExceptionCode.INVALIDPARAMETERVALUE);
274: }
275:
276: } else {
277: outputFormat = "text/xml";
278: }
279: boolean store = getNodeAsBoolean(getRootElement(),
280: "@store", nsContext, true);
281: this .transformRequest = new Transform(version, requestId,
282: store, sourceCRS, targetCRS, inputData,
283: transformableData, outputFormat, dataPresentation);
284:
285: } catch (XMLParsingException e) {
286: LOG.logError(e.getMessage(), e);
287: throw new OGCWebServiceException(e.getMessage(),
288: ExceptionCode.NOAPPLICABLECODE);
289: } catch (UnknownCRSException e) {
290: LOG.logError(e.getMessage(), e);
291: throw new OGCWebServiceException(e.getMessage(),
292: ExceptionCode.NOAPPLICABLECODE);
293: }
294: }
295:
296: /**
297: * Parses the deegree inlinedata element.
298: *
299: * @param sourceCRS
300: * of the data.
301: * @param targetCRS
302: * in which the data is to be transformed.
303: * @param inlineData
304: * element to extract the data from.
305: * @return a {@link TransformableData} element instantiated with the right type.
306: * @throws OGCWebServiceException
307: * if for any reason the data could not be parsed or processed.
308: */
309: private TransformableData<?> parseInlineData(
310: CoordinateSystem sourceCRS, CoordinateSystem targetCRS,
311: Node inlineData) throws OGCWebServiceException {
312: Node firstChild = null;
313: try {
314: firstChild = getElement(inlineData, "*[1]", nsContext);
315: } catch (XMLParsingException e) {
316: LOG.logError(e.getMessage(), e);
317: }
318: if (firstChild != null) {
319: LOG.logDebug("Incoming inlineData has localname: "
320: + inlineData.getLocalName()
321: + " has a firstchild with localname: "
322: + firstChild.getLocalName());
323: String prefix = firstChild.getPrefix();
324: String nameSpace = firstChild.getNamespaceURI();
325: if (prefix != null) {
326: String tmp = firstChild.lookupNamespaceURI(prefix);
327: if (tmp != null && !"".equals(tmp)) {
328: nameSpace = tmp;
329: }
330: }
331: if (nameSpace == null) {
332: nameSpace = "";
333: }
334: LOG.logDebug("Firstchild is bound to namespace: "
335: + nameSpace);
336: if (!DEEGREEWCTS.toASCIIString().equalsIgnoreCase(
337: nameSpace.trim())) {
338: LOG
339: .logError("The node beneath an "
340: + DEEGREEWCTS_PREFIX
341: + ":inlineData element must be bound to the deegree-wcts ("
342: + DEEGREEWCTS.toASCIIString()
343: + ") name space, found following namespace: "
344: + nameSpace);
345: } else {
346: String localName = firstChild.getLocalName();
347: if (localName != null) {
348: localName = localName.trim();
349: if ("SimpleData".equals(localName)) {
350: SimpleData result = parseSimpleData(sourceCRS,
351: targetCRS, (Element) firstChild);
352: if (result == null) {
353: result = new SimpleData(sourceCRS,
354: targetCRS, null, null, null);
355: }
356: return result;
357: } else if ("GeometryData".equals(localName)) {
358: return new GeometryData(sourceCRS, targetCRS,
359: parseGeometryData(sourceCRS
360: .getIdentifier(),
361: (Element) firstChild));
362: } else if ("FeatureCollectionData"
363: .equals(localName)) {
364: return new FeatureCollectionData(
365: sourceCRS,
366: targetCRS,
367: parseFeatureCollectionData((Element) firstChild));
368: } else {
369: throw new OGCWebServiceException(
370: Messages
371: .getMessage(
372: "WCTS_TRANSFORM_UNKNOWN_INLINE_DATA",
373: localName,
374: "SimpleData, GeometryData or FeatureCollectionData"),
375: WCTSExceptionCode.INVALIDPARAMETERVALUE);
376: }
377:
378: }
379: }
380: }
381: throw new OGCWebServiceException(Messages
382: .getMessage("WCTS_TRANSFORM_MISSING_DATA"),
383: WCTSExceptionCode.NO_INPUT_DATA);
384: }
385:
386: /**
387: * @param simpleData
388: * the dom-xml element to be parsed.
389: * @return a list of point3d's or <code>null</code> if the given parameter <code>null</code>.
390: * @throws OGCWebServiceException
391: * if the number of points is not congruent with the dimension.
392: */
393: private SimpleData parseSimpleData(CoordinateSystem sourceCRS,
394: CoordinateSystem targetCRS, Element simpleData)
395: throws OGCWebServiceException {
396: if (simpleData == null) {
397: return null;
398: }
399:
400: int sourceDim = sourceCRS.getDimension();
401: List<Point3d> transformableData = new ArrayList<Point3d>();
402: String cs = simpleData.getAttribute("cs");
403: if (cs == null || "".equals(cs)) {
404: cs = ",";
405: }
406: String ts = simpleData.getAttribute("ts");
407: if (ts == null || "".equals(ts)) {
408: ts = " ";
409: }
410: String values = getStringValue(simpleData);
411: values = values.trim();
412: String[] tuples = values.split(ts);
413:
414: int count = 0;
415: for (String tuple : tuples) {
416: count++;
417: if (tuple != null) {
418: tuple = tuple.trim();
419: String[] coords = tuple.split(cs);
420: if (coords.length != sourceDim) {
421: throw new OGCWebServiceException(Messages
422: .getMessage(
423: "WCTS_DIM_COORDS_NOT_CONGRUENT", ""
424: + sourceDim, cs),
425: WCTSExceptionCode.INVALIDPARAMETERVALUE);
426: }
427: String first = coords[0];
428: String second = coords[1];
429: String third = null;
430: if (sourceDim == 3) {
431: third = coords[2];
432: }
433: double x = 0;
434: double y = 0;
435: double z = 0;
436: try {
437: x = Double.parseDouble(first);
438: } catch (NumberFormatException e) {
439: LOG.logError("Unparsable x value: " + x
440: + " at coord " + count);
441: }
442: try {
443: y = Double.parseDouble(second);
444: } catch (NumberFormatException e) {
445: LOG.logError("Unparsable y value: " + y
446: + " at coord " + count);
447: }
448: if (sourceDim == 3) {
449: try {
450: z = Double.parseDouble(third);
451: } catch (NumberFormatException e) {
452: LOG.logError("Unparsable z value: " + z
453: + " at coord " + count);
454: }
455: }
456: transformableData.add(new Point3d(x, y, z));
457: }
458: }
459: return new SimpleData(sourceCRS, targetCRS, transformableData,
460: cs, ts);
461: }
462:
463: /**
464: * Parse the featurecollections from the given featureCollectionsData element.
465: *
466: * @param featureCollectionData
467: * (a deegreewcts:inlineElement/deegreewcts:FeatureCollectionData or a deegreewcts:mulipart element).
468: * @return the list of feature collections.
469: * @throws OGCWebServiceException
470: * if no feature collections were found or an xml parsing exception occurred.
471: */
472: private List<FeatureCollection> parseFeatureCollectionData(
473: Element featureCollectionData)
474: throws OGCWebServiceException {
475: if (featureCollectionData == null) {
476: return null;
477: }
478: List<FeatureCollection> transformableData = new ArrayList<FeatureCollection>();
479: try {
480: List<Element> fcElements = getElements(
481: featureCollectionData, GML_PREFIX
482: + ":FeatureCollection", nsContext);
483: if (fcElements == null || fcElements.size() == 0) {
484: LOG
485: .logError("Could not find any feature collections, this is strange!");
486: throw new OGCWebServiceException(Messages.getMessage(
487: "WCTS_TRANSFORM_NO_DATA_FOUND",
488: "gml:FeatureCollection"),
489: WCTSExceptionCode.NO_INPUT_DATA);
490: }
491: GMLFeatureCollectionDocument fd = new GMLFeatureCollectionDocument(
492: true, true);
493: for (Element fc : fcElements) {
494: fd.setRootElement(fc);
495: FeatureCollection data = fd.parse();
496: if (data != null) {
497: transformableData.add(data);
498: }
499: }
500: } catch (XMLParsingException e) {
501: LOG.logError(e.getMessage(), e);
502: throw new OGCWebServiceException(e.getMessage(),
503: WCTSExceptionCode.NOAPPLICABLECODE);
504: }
505: return transformableData;
506: }
507:
508: /**
509: * Parse the geometries from the given geometries data element.
510: *
511: * @param sourceCRSID
512: * needed for the wrap function of the GMLGeometrieAdapter.
513: * @param geometryData
514: * (an deegreewcts:inlineElement/deegreewcts:GeometryData or a deegreewcts:mulipart element).
515: * @return the list of geometries.
516: * @throws OGCWebServiceException
517: * if no feature collections were found or an xml parsing exception occurred.
518: */
519: private List<Geometry> parseGeometryData(String sourceCRSID,
520: Element geometryData) throws OGCWebServiceException {
521: if (geometryData == null) {
522: return null;
523: }
524: List<Geometry> transformableData = new ArrayList<Geometry>();
525: try {
526: List<Element> geomElements = getElements(geometryData, "*",
527: nsContext);
528: if (geomElements == null || geomElements.size() == 0) {
529: LOG
530: .logError("Could not find any geometries, this is strange!");
531: throw new OGCWebServiceException(Messages.getMessage(
532: "WCTS_TRANSFORM_NO_DATA_FOUND",
533: "gml:Geometries"),
534: WCTSExceptionCode.NO_INPUT_DATA);
535: }
536: for (Element fc : geomElements) {
537: try {
538: Geometry data = GMLGeometryAdapter.wrap(fc,
539: sourceCRSID);
540: if (data != null) {
541: transformableData.add(data);
542: }
543: } catch (GeometryException e) {
544: LOG.logError(e.getMessage(), e);
545: }
546: }
547: } catch (XMLParsingException e) {
548: LOG.logError(e.getMessage(), e);
549: throw new OGCWebServiceException(e.getMessage(),
550: WCTSExceptionCode.NOAPPLICABLECODE);
551: }
552: return transformableData;
553: }
554:
555: /**
556: * @return the transformRequest may be <code>null</code>.
557: */
558: public final Transform getTransformRequest() {
559: return transformRequest;
560: }
561:
562: }
|