Package com.orci.geoserver.wfs.getnearest

Source Code of com.orci.geoserver.wfs.getnearest.GetNearestResponse

/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org.  All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package com.orci.geoserver.wfs.getnearest;

import java.io.IOException;
import java.io.OutputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.logging.Logger;

import javax.units.Converter;
import javax.units.SI;
import javax.units.Unit;
import javax.units.UnitFormat;

import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureSource;
import org.geotools.factory.FactoryFinder;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.FeatureType;
import org.geotools.feature.FeatureTypeBuilder;
import org.geotools.feature.GeometryAttributeType;
import org.geotools.feature.SchemaException;
import org.geotools.feature.type.NumericAttributeType;
import org.geotools.filter.AttributeExpression;
import org.geotools.filter.FidFilter;
import org.geotools.filter.Filter;
import org.geotools.filter.FilterFactory;
import org.geotools.filter.FilterFactoryFinder;
import org.geotools.filter.LiteralExpression;
import org.geotools.measure.Measure;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.filter.And;
import org.opengis.filter.spatial.DWithin;
import org.vfny.geoserver.Request;
import org.vfny.geoserver.Response;
import org.vfny.geoserver.ServiceException;
import org.vfny.geoserver.global.AttributeTypeInfo;
import org.vfny.geoserver.global.Data;
import org.vfny.geoserver.global.FeatureTypeInfo;
import org.vfny.geoserver.global.GeoServer;
import org.vfny.geoserver.global.NameSpaceInfo;
import org.vfny.geoserver.global.Service;
import org.vfny.geoserver.global.WFS;
import org.vfny.geoserver.wfs.Query;
import org.vfny.geoserver.wfs.WfsException;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.operation.distance.DistanceOp;


/**
* Handles a Get Feature request and creates a Get Feature response GML
* string.
*
* @author Chris Holmes, TOPP
* @author Jody Garnett, Refractions Research
* @version $Id: GetNearestResponse.java,v 1.24 2004/04/05 12:03:19 cholmesny Exp $
*/
public class GetNearestResponse implements Response {
    /** Standard logging instance for class */
    private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.responses");
    GetNearestResponseDelegate delegate;
    String featureTypeName;

    /**
     * This is the request provided to the execute( Request ) method.<p>We
     * save it so we can access the handle provided by the user for error
     * reporting during the writeTo( OutputStream ) opperation.</p>
     <p>This value will be <code>null</code> until execute is
     * called.</p>
     */
    private GetNearestRequest request;

    /**
             * Empty constructor
             */
    public GetNearestResponse() {
        request = null;
    }

    /**
     * Returns any extra headers that this service might want to set in
     * the HTTP response object.
     *
     * @see org.vfny.geoserver.Response#getResponseHeaders()
     */
    public HashMap getResponseHeaders() {
        return null;
    }

    /**
     * DOCUMENT ME!
     *
     * @param gs DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getContentType(GeoServer gs) {
        return delegate.getContentType(gs);
    }

    public String getContentEncoding() {
        return delegate.getContentEncoding();
    }

    /**
     * Jody here with one pass replacement for writeTo.<p>This code is
     * a discussion point, when everyone has had there input we will try and
     * set things up properly.</p>
     <p>I am providing a mirror of the existing desing: - execute
     * gathers the resultList - sets up the header</p>
     *
     * @param out DOCUMENT ME!
     *
     * @throws ServiceException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     * @throws IllegalStateException DOCUMENT ME!
     */
    public void writeTo(OutputStream out) throws ServiceException, IOException {
        if ((request == null) || (delegate == null)) {
            throw new IllegalStateException("execute has not been called prior to writeTo");
        }

        delegate.encode(out);
    }

    /**
     * Executes GetNearestRequest.<p>Willing to execute a FetureRequest,
     * or GetNearestRequestWith Lock.</p>
     *
     * @param req DOCUMENT ME!
     *
     * @throws ServiceException DOCUMENT ME!
     */
    public void execute(Request req) throws ServiceException {
        execute((GetNearestRequest) req);
    }

    /**
     * use the SPI mechanism to get a GetNearestResponseDelegate for the
     * specified output format.
     *
     * @param outputFormat
     *
     * @return
     *
     * @throws NoSuchElementException DOCUMENT ME!
     */
    public static GetNearestResponseDelegate getDelegate(String outputFormat)
        throws NoSuchElementException {
        GetNearestResponseDelegateProducerSpi spi;
        Iterator spi_it = FactoryFinder.factories(GetNearestResponseDelegateProducerSpi.class);

        while (spi_it.hasNext()) {
            spi = (GetNearestResponseDelegateProducerSpi) spi_it.next();
            if (spi.canProduce(outputFormat)) {
                return spi.createFeatureDelegateProducer(outputFormat);
            }
        }

        throw new NoSuchElementException();
    }

    /**
     * Performs a getFeatures, or getFeaturesWithLock (using gt2
     * locking ).<p>The idea is to grab the FeatureResulsts during
     * execute, and use them during writeTo.</p>
     *
     * @param request
     *
     * @throws ServiceException
     * @throws WfsException DOCUMENT ME!
     *
     * @task TODO: split this up a bit more?  Also get the proper namespace
     *       declrations and schema locations.  Right now we're back up to
     *       where we were with 1.0., as we can return two FeatureTypes in the
     *       same namespace.  CITE didn't check for two in different
     *       namespaces, and gml builder just couldn't deal.  Now we should be
     *       able to, we just need to get the reporting right, use the
     *       AllSameType function as  Describe does.
     */
    public void execute(GetNearestRequest request) throws ServiceException {
        LOGGER.finest("execute GetNearestRequest response. Called request is: " + request);
        this.request = request;
        this.featureTypeName = null;

        String outputFormat = request.getOutputFormat();

        try {
            delegate = GetNearestResponse.getDelegate(outputFormat);
        } catch (NoSuchElementException ex) {
            throw new WfsException("output format: " + outputFormat + " not "
                + "supported by geoserver", ex);
        }

        GetNearestResults results = new GetNearestResults(request);

        //
        // Optimization Idea
        //
        // We should be able to reduce this to a two pass opperations.
        //
        // Pass #1 execute
        // - Attempt to Locks Fids during the first pass
        // - Also collect Bounds information during the first pass
        //
        // Pass #2 writeTo
        // - Using the Bounds to describe our FeatureCollections
        // - Iterate through FeatureResults producing GML
        //
        // And allways remember to release locks if we are failing:
        // - if we fail to aquire all the locks we will need to fail and
        //   itterate through the the FeatureSources to release the locks
        //
        WFS wfs = request.getWFS();
        GeoServer config = wfs.getGeoServer();
        Data catalog = wfs.getData();
        FeatureTypeInfo meta = null;
        NameSpaceInfo namespace;
        Query query;

        FeatureSource source;
        Feature feature;
        String fid;
        FilterFactory filterFactory = FilterFactoryFinder.createFilterFactory();
        FidFilter fidFilter;

        try {
            for (Iterator it = request.getQueries().iterator(); it.hasNext();) {
                query = (Query) it.next();

                // the feature type name used in the content disposition response will match
                // the first feature type
                if (featureTypeName == null) {
                    featureTypeName = query.getTypeName();
                }

                meta = catalog.getFeatureTypeInfo(query.getTypeName());
                namespace = meta.getDataStoreInfo().getNameSpace();
                source = meta.getFeatureSource();

                List attrs = meta.getAttributes();

                List propNames = query.getPropertyNames(); // REAL LIST: be careful here :)
                List attributeNames = meta.getAttributeNames();

                for (Iterator iter = propNames.iterator(); iter.hasNext();) {
                    String propName = (String) iter.next();

                    if (!attributeNames.contains(propName)) {
                        String mesg = "Requested property: " + propName + " is "
                            + "not available for " + query.getTypeName() + ".  "
                            + "The possible propertyName values are: " + attributeNames;
                        throw new WfsException(mesg);
                    }
                   
                   
                }

                List extraGeometries = new ArrayList();
                List properties = new ArrayList();
                if (propNames.size() != 0) {
                    Iterator ii = attrs.iterator();
                   

                    while (ii.hasNext()) {
                        AttributeTypeInfo ati = (AttributeTypeInfo) ii.next();

                        //String attName = (String) ii.next();
                        LOGGER.finer("checking to see if " + propNames + " contains" + ati);

                        if (((ati.getMinOccurs() > 0) && (ati.getMaxOccurs() != 0))
                                || propNames.contains(ati.getName())) {
                            properties.add(ati.getName());
                        }
                       
                        //if(wfs.isFeatureBounding() && meta.getFeatureType().getAttributeType(ati.getName()) instanceof GeometryAttributeType
                        //        && !properties.contains(ati.getName())) {
                        //    properties.add(ati.getName());
                        //    extraGeometries.add(ati.getName());
                        //}
                        if(meta.getFeatureType().getAttributeType(ati.getName()) instanceof GeometryAttributeType
                                && !properties.contains(ati.getName())) {
                            properties.add(ati.getName());
                            extraGeometries.add(ati.getName());
                        }
                    }

                    query.setPropertyNames(properties);
                }
               
                // Add range to filter
                AttributeExpression geomAttb = filterFactory.createAttributeExpression(meta.getFeatureType(), meta.getFeatureType().getDefaultGeometry().getName());
                LiteralExpression pointExpr = filterFactory.createLiteralExpression(request.getPoint());
                DWithin dWithin = filterFactory.dwithin(geomAttb, pointExpr, request.getMaxRange(), request.getUnits());
                if (query.getFilter() == null) {
                    query.addFilter((Filter)dWithin);
                   
                } else {
                    And andFilter = filterFactory.and(Arrays.asList(new Filter[] { (Filter)dWithin, query.getFilter() }));
                    query.addFilter((Filter)andFilter);
                }
               
                LOGGER.fine("Query is " + query + "\n To gt2: " + query.toDataQuery(Integer.MAX_VALUE));

                //DJB: note if maxFeatures gets to 0 the while loop above takes care of this! (this is a subtle situation)
                FeatureCollection featuresCheck = source.getFeatures(query.toDataQuery(Integer.MAX_VALUE));
               
                // find nearest feature
                Unit fromUnit = SI.METER;
                Unit toUnit = UnitFormat.getInstance().parseUnit(request.getUnits());
                Converter unitConvert = fromUnit.getConverterTo(toUnit);
                Feature nearestFeature = null;
                double nearestDistance = 9e9;
                double nearestBearing = 0;
                for (Iterator sItr = featuresCheck.iterator(); sItr.hasNext();) {
                    Feature f = (Feature)sItr.next();
                    if (f.getDefaultGeometry() == null) continue;
                    DistanceOp op = new DistanceOp(request.getPoint(), f.getDefaultGeometry());
                    Coordinate[] co = op.closestPoints();
                    Measure m = DefaultGeographicCRS.WGS84.distance(new double[] { co[0].x, co[0].y, }, new double[] { co[1].x, co[1].y, });
                    if (m.doubleValue() > nearestDistance) continue;
                    nearestFeature = f;
                    nearestDistance = m.doubleValue();
                    nearestBearing = calcBearing(co);
                }

                //GR: I don't know if the featuresults should be added here for later
                //encoding if it was a lock request. may be after ensuring the lock
                //succeed?
                FeatureCollection features = FeatureCollections.newCollection();
                if (nearestFeature != null) features.add(superFeature(nearestFeature, unitConvert.convert(nearestDistance), nearestBearing));
               
                // we may need to shave off geometries we did load only to make bounds
                // computation happy
                if(extraGeometries.size() > 0) {
                    List residualProperties = new ArrayList(properties);
                    residualProperties.removeAll(extraGeometries);
                    residualProperties.add("nearest_distance");
                    residualProperties.add("nearest_bearing");
                    String[] residualNames = (String[]) residualProperties.toArray(new String[residualProperties.size()]);
                    FeatureType targetType = DataUtilities.createSubType(superFeatureType(meta.getFeatureType()), residualNames);
                    features = new FeatureBoundsFeatureCollection(features, targetType);
                }

                results.addFeatures(meta, features);
            }

            //end for
            //prepare to encode in the desired output format
            delegate.prepare(outputFormat, results);
        } catch (IOException e) {
            throw new ServiceException(e, "problem with FeatureResults", request.getHandle());
        } catch (NoSuchElementException e) {
            throw new ServiceException(e, "problem with FeatureResults", request.getHandle());
        } catch (SchemaException e) {
            throw new ServiceException(e, "problem with FeatureResults", request.getHandle());
        } catch (ParseException e) {
            throw new ServiceException(e, "problem with FeatureResults", request.getHandle());
        }
    }
   
    private FeatureType superFeatureType(FeatureType oldType) {
        FeatureType featureType = null;
        try {
            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(oldType.getTypeName());
            typeBuilder.setNamespace(oldType.getNamespace());

            //typeBuilder.setDefaultGeometry(oldType.getDefaultGeometry());
            for (int i = 0; i < oldType.getAttributeCount(); i++) {
                //if (oldType.getAttributeType(i).equals(oldType.getDefaultGeometry())) continue;
                typeBuilder.addType(oldType.getAttributeType(i));
            }
            typeBuilder.addType(new NumericAttributeType("nearest_distance", Double.class, false, 0, 999, null, null));       
            typeBuilder.addType(new NumericAttributeType("nearest_bearing", Double.class, false, 0, 999, null, null));       

            featureType = typeBuilder.getFeatureType();
        } catch (Exception e) {
            LOGGER.severe("Error createing super feature type: " + e); e.printStackTrace();
        }
        return featureType;
    }
   
    private Feature superFeature(Feature oldFeature, Double distance, Double bearing) {
        FeatureType featureType = superFeatureType(oldFeature.getFeatureType());
        Object[] attbs = new Object[featureType.getAttributeCount()];
        for (int i = 0; i < oldFeature.getFeatureType().getAttributeCount(); i++) {
            attbs[i] = oldFeature.getAttribute(i);
        }
        attbs[attbs.length - 2] = distance;
        attbs[attbs.length - 1] = bearing;
        try {
            return featureType.create(attbs, oldFeature.getID());
        } catch (Exception e) {
            LOGGER.severe("Error createing super feature: " + e); e.printStackTrace();
        }
        return null;
    }
   
    private double calcBearing(Coordinate[] coords) {
        double y = Math.sin(coords[0].x - coords[1].x) * Math.cos(coords[1].y);
        double x = Math.cos(coords[0].y)*Math.sin(coords[1].y) - Math.sin(coords[0].y)*Math.cos(coords[1].y)*Math.cos(coords[0].x - coords[1].x);
        double brng = ((Math.atan2(y, x) * 180.0 / Math.PI) + 360) % 360;
        return brng;
    }

    /**
     * Release locks if we are into that sort of thing.
     *
     * @see org.vfny.geoserver.Response#abort()
     */
    public void abort(Service gs) {
    }

    public String getContentDisposition() {
        if ((featureTypeName != null) && (featureTypeName.indexOf(':') != -1)) {
            featureTypeName = featureTypeName.substring(featureTypeName.indexOf(':') + 1);
        }

        return delegate.getContentDisposition(featureTypeName);
    }
}
TOP

Related Classes of com.orci.geoserver.wfs.getnearest.GetNearestResponse

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.