WfsGetFeatureRequest.java

package de.turnertech.ows.servlet;

import java.io.IOException;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;

import de.turnertech.ows.common.ExceptionCode;
import de.turnertech.ows.common.Model;
import de.turnertech.ows.common.OwsContext;
import de.turnertech.ows.common.OwsRequestContext;
import de.turnertech.ows.common.RequestHandler;
import de.turnertech.ows.gml.Envelope;
import de.turnertech.ows.gml.FeatureType;
import de.turnertech.ows.gml.IFeature;
import de.turnertech.ows.parameter.ResultType;
import de.turnertech.ows.parameter.WfsRequestParameter;
import de.turnertech.ows.parameter.WfsVersionValue;
import de.turnertech.ows.srs.SpatialReferenceSystemFormat;
import de.turnertech.ows.srs.SpatialReferenceSystemRepresentation;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class WfsGetFeatureRequest implements RequestHandler {
    
    private static Optional<Envelope> getBoundingBox(HttpServletRequest request) {
        String bboxTypeString = WfsRequestParameter.findValue(request, WfsRequestParameter.BBOX).orElse(null);
        if(bboxTypeString == null || bboxTypeString.trim().equals("")) {
            return Optional.empty();
        }
        String[] parts = bboxTypeString.split(",", 5);
        return Optional.of(new Envelope(Double.valueOf(parts[1]), Double.valueOf(parts[0]), Double.valueOf(parts[3]), Double.valueOf(parts[2])));
    }

    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response, OwsContext owsContext, OwsRequestContext requestContext) throws ServletException, IOException {
        final String resultTypeString = WfsRequestParameter.findValue(request, WfsRequestParameter.RESULTTYPE).orElse(ResultType.RESULTS.toString());
        final ResultType resultType = ResultType.valueOfIgnoreCase(resultTypeString);
        SpatialReferenceSystemRepresentation targetSrs = requestContext.getRequestedSrs();
        SpatialReferenceSystemFormat srsFormat = requestContext.getOwsVersion() == WfsVersionValue.V2_0_0 ? SpatialReferenceSystemFormat.URN : SpatialReferenceSystemFormat.URI;

        List<FeatureType> typenames = requestContext.getFeatureTypes();

        Optional<Envelope> requestedBoundingBox = getBoundingBox(request);

        Envelope actualBoundingBox = null;
    
        response.setContentType(RequestHandler.CONTENT_XML);

        Collection<IFeature> features = new LinkedList<>();
        for (FeatureType featureType : typenames) {
            Model model = owsContext.getModelProvider().getModel(featureType);
            if(requestedBoundingBox.isPresent()) {
                features.addAll(model.filter(requestedBoundingBox.get()));
            } else {
                features.addAll(model.getAll());
                if(actualBoundingBox == null) {
                    actualBoundingBox = model.getBoundingBox();
                }
                if(actualBoundingBox != null) {
                    actualBoundingBox.expandToFit(model.getBoundingBox());
                }
            }
        }

        response.setContentType(RequestHandler.CONTENT_XML);
        XMLStreamWriter out = null;
        try {
            out = XMLOutputFactory.newInstance().createXMLStreamWriter(response.getOutputStream(), "UTF-8");
            out.writeStartDocument("UTF-8", "1.0");
            if(ResultType.HITS == resultType) {
                out.writeEmptyElement("FeatureCollection");
            } else {
                out.writeStartElement("FeatureCollection");
                out.writeNamespace(owsContext.getXmlNamespacePrefix(OwsContext.GML_URI), OwsContext.GML_URI);
                List<String> writtenNamespaces = new ArrayList<>(features.size());
                for(FeatureType typename : typenames) {
                    if(!writtenNamespaces.contains(typename.getNamespace())) {
                        out.writeNamespace(owsContext.getXmlNamespacePrefix(typename.getNamespace()), typename.getNamespace());
                        writtenNamespaces.add(typename.getNamespace());
                    }
                }
            }
            out.writeDefaultNamespace(OwsContext.WFS_URI);

            final String wfsSchema = owsContext.getXmlNamespaceSchema(OwsContext.WFS_URI);
            if(wfsSchema != null) {
                out.writeNamespace(owsContext.getXmlNamespacePrefix(OwsContext.XSI_URI), OwsContext.XSI_URI);
                out.writeAttribute("xsi:schemaLocation", OwsContext.WFS_URI + " " + wfsSchema);
            }

            out.writeAttribute(OwsContext.WFS_URI, "timeStamp", OffsetDateTime.now().truncatedTo(ChronoUnit.SECONDS).atZoneSameInstant(ZoneOffset.UTC).toString());
            out.writeAttribute(OwsContext.WFS_URI, "numberMatched", Integer.toString(features.size()));
            out.writeAttribute(OwsContext.WFS_URI, "numberReturned", ResultType.HITS == resultType ? "0" : Integer.toString(features.size()));

            if(ResultType.RESULTS == resultType) {

                SpatialReferenceSystemRepresentation bboxSrs = null;
                if(targetSrs == null) {
                    if(typenames.size() == 1) {
                        bboxSrs = new SpatialReferenceSystemRepresentation(typenames.get(0).getSrs(), srsFormat);
                    }
                } else {
                    bboxSrs = targetSrs;
                }

                if(features.size() > 0 && requestedBoundingBox.isPresent()) {
                    requestedBoundingBox.get().writeGml(out, Envelope.GML_NAME, Envelope.NAMESPACE, bboxSrs);
                } else if(features.size() > 0 && actualBoundingBox != null) {
                    actualBoundingBox.writeGml(out, Envelope.GML_NAME, Envelope.NAMESPACE, bboxSrs);
                }

                for (IFeature feature : features) {
                    out.writeStartElement(OwsContext.GML_URI, "featureMember");
                    feature.writeGml(out, feature.getFeatureType().getName(), feature.getFeatureType().getNamespace(), targetSrs == null ? new SpatialReferenceSystemRepresentation(feature.getFeatureType().getSrs(), srsFormat) : targetSrs);
                    out.writeEndElement();
                }
            }

            if(ResultType.HITS != resultType) {
                out.writeEndElement();
            }

            out.writeEndDocument();
        } catch (Exception e) {
            response.sendError(500, ErrorServlet.encodeMessage(ExceptionCode.OPERATION_PROCESSING_FAILED.toString(), "GetFeatures", "XML Construction of the response failed."));
        }
    }
}