/**
* Copyright (c) Codice Foundation
*
* This 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, either version 3 of the
* License, or any later version.
*
* This program 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. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*
**/
package org.codice.ddf.spatial.ogc.csw.catalog.common;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
/**
* Parses OWS Bounding Box geometry XML and converts it to WKT.
*
* Bounding Box XML is of the form:
*
* <pre>
* {@code
* <ows:BoundingBox crs="urn:x-ogc:def:crs:EPSG:6.11:4326">
* <ows:LowerCorner>60.042 13.754</ows:LowerCorner>
* <ows:UpperCorner>68.410 17.920</ows:UpperCorner>
* </ows:BoundingBox>
* }
* </pre>
*
* @author rodgersh
*
*/
public class BoundingBoxReader {
private static final transient Logger LOGGER = LoggerFactory.getLogger(BoundingBoxReader.class);
private static final String SPACE = " ";
private HierarchicalStreamReader reader;
private boolean isLonLatOrder;
public BoundingBoxReader(HierarchicalStreamReader reader, boolean isLonLatOrder) {
this.reader = reader;
this.isLonLatOrder = isLonLatOrder;
}
public String getWkt() {
String wkt = null;
// reader should initially be positioned at <ows:BoundingBox> element
LOGGER.debug("Initial node name = {}", reader.getNodeName());
if (!reader.getNodeName().contains("BoundingBox")) {
return null;
}
String crs = reader.getAttribute("crs");
// TODO: CRS value determines order of bounding box values, i.e.,
// WGS-84 specifies lon,lat order
// EPSG:4326 specifies lat,lon order
// For now we assume lat,lon order
LOGGER.debug("crs = {}", crs);
// Move down to the first child node of <BoundingBox>, which should
// be the <LowerCorner> tag
reader.moveDown();
// Parse LowerCorner position from bounding box
String[] lowerCornerPosition = null;
if (reader.getNodeName().contains("LowerCorner")) {
String value = reader.getValue();
lowerCornerPosition = getCoordinates(value, isLonLatOrder);
}
// Move back up to the <BoundingBox> parent tag
reader.moveUp();
// Move down to the next child node of the <BoundingBox> tag, which
// should be the <UpperCorner> tag
reader.moveDown();
// Parse UpperCorner position from bounding box
String[] upperCornerPosition = null;
if (reader.getNodeName().contains("UpperCorner")) {
String value = reader.getValue();
upperCornerPosition = getCoordinates(value, isLonLatOrder);
}
// If both corner positions parsed, then compute other 2 corner
// positions of the
// bounding box and create the WKT for the bounding box.
if (lowerCornerPosition != null && lowerCornerPosition.length == 2
&& upperCornerPosition != null && upperCornerPosition.length == 2) {
/**
* The WKT created here need to be in LON/LAT. This is the order that we store WKT in
* the metacard location field.
*/
wkt = createWkt(lowerCornerPosition, upperCornerPosition);
}
// Move position back up to the parent <BoundingBox> tag, where we
// started
reader.moveUp();
LOGGER.debug("Returning WKT in LON/LAT coord order: {}.", wkt);
return wkt;
}
/**
* We want to create WKT in LON/LAT order.
*
* Points for bounding box will be computed starting with the lower corner, and proceeding in a
* clockwise rotation. So lower corner point would be point 1, upper corner point would be point
* 3, and points 2 and 4 would be computed.
*/
private String createWkt(String[] lowerCornerPosition, String[] upperCornerPosition) {
LOGGER.debug("Creating WKT in LON/LAT coordinate order.");
if (upperCornerPosition[0].equals(lowerCornerPosition[0]) && upperCornerPosition[1]
.equals(lowerCornerPosition[1])) {
return "POINT(" + lowerCornerPosition[0] + SPACE + lowerCornerPosition[1] + ")";
}
return "POLYGON((" + lowerCornerPosition[0] + SPACE + lowerCornerPosition[1] + ", "
+ upperCornerPosition[0] + SPACE + lowerCornerPosition[1] + ", "
+ upperCornerPosition[0] + SPACE + upperCornerPosition[1] + ", "
+ lowerCornerPosition[0] + SPACE + upperCornerPosition[1] + ", "
+ lowerCornerPosition[0] + SPACE + lowerCornerPosition[1] + "))";
}
/**
* @param coords
* The latitude and longitude coordinates (in no particular order).
* @param areCoordsInLonLatOrder
* True if the source returns the coordinates in LON/LAT order; false, otherwise.
* @return The coordinates in LON/LAT order.
*/
private String[] getCoordinates(String coords, boolean areCoordsInLonLatOrder) {
if (!areCoordsInLonLatOrder) {
/**
* We want to create WKT in LON/LAT order. Since the response has the coords in LAT/LON
* order, we need to reverse them.
*/
return StringUtils.reverseDelimited(coords, SPACE.charAt(0)).split(SPACE);
} else {
/**
* We want to create WKT in LON/LAT order. Since this is the order of the coords in the
* response, we use them as-is.
*/
return coords.split(SPACE);
}
}
}