org.geotools.data.sfs.SFSDataStore.java Source code

Java tutorial

Introduction

Here is the source code for org.geotools.data.sfs.SFSDataStore.java

Source

/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2002-2011, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library 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;
 *    version 2.1 of the License.
 *
 *    This library 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.
 */
package org.geotools.data.sfs;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

import org.apache.commons.codec.binary.Base64;
import org.geotools.data.store.ContentDataStore;
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.feature.NameImpl;
import org.geotools.filter.FilterCapabilities;
import org.json.simple.JSONArray;
import org.json.simple.parser.JSONParser;
import org.opengis.feature.type.Name;
import org.opengis.filter.And;
import org.opengis.filter.ExcludeFilter;
import org.opengis.filter.IncludeFilter;
import org.opengis.filter.PropertyIsLike;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.Intersects;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import com.vividsolutions.jts.geom.Envelope;

/**
 * This DataStore is based on OpenDataStore protocol using GeoJSON as the
 * encoding format loosely based on RESTFUL principles. This code expanded on 
 * from GeoREST in Unsupported Modules.
 * @author 
 *
 *
 *
 * @source $URL$
 */
public class SFSDataStore extends ContentDataStore {

    /**
     *  The store filter capabilities
     *  @return The filter capabilities, never <code>null</code>.
     */
    static FilterCapabilities ODS_FILTER_CAPABILITIES = new FilterCapabilities() {

        {

            addAll(FilterCapabilities.SIMPLE_COMPARISONS_OPENGIS);

            addType(And.class);

            // TODO: check these two filters are actually supported in the encoder
            addType(BBOX.class);
            addType(Intersects.class);

            addType(IncludeFilter.class);
            addType(ExcludeFilter.class);
            addType(PropertyIsLike.class);
        }
    };

    protected static final Logger LOGGER = org.geotools.util.logging.Logging
            .getLogger("org.geotools.data.simplefeatureservice");
    String baseURL;
    Map<Name, SFSLayer> layers = new LinkedHashMap<Name, SFSLayer>();
    String user;
    String password;
    int timeout;

    /**
     * Constructor -- it takes in the base URl and then appends "/capabilities"
     * to pull the list of available layers
     * @param URL 
     * 
     */
    public SFSDataStore(URL fnURL, String namespaceURI) throws IOException {
        String strURL = fnURL.toString();
        this.baseURL = strURL + (strURL.endsWith("/") ? "" : "/");
        if (baseURL.endsWith("capabilities/")) {
            baseURL = baseURL.substring(0, baseURL.length() - "capabilities/".length());
        }
        this.namespaceURI = namespaceURI;
        processCapabilities();
    }

    /**
     * Constructor -- it takes in the base URl and then appends "/capabilities"
     * to pull the list of available layers
     * @param URL 
     * 
     */
    public SFSDataStore(URL fnURL, String namespaceURI, String user, String password, int timeout)
            throws IOException {
        String strURL = fnURL.toString();
        this.baseURL = strURL + (strURL.endsWith("/") ? "" : "/");
        if (baseURL.endsWith("capabilities/")) {
            baseURL = baseURL.substring(0, baseURL.length() - "capabilities/".length());
        }
        this.namespaceURI = namespaceURI;
        this.user = user;
        this.password = password;
        this.timeout = timeout;
        processCapabilities();
    }

    /**
     * Constructor -- it simply uses the json response from the server to extract
     * the layer information. Only used for testing, not exposing it to the whole world
     * @param String
     * 
     */
    SFSDataStore(String json, String namespaceURI) throws IOException {
        this.namespaceURI = namespaceURI;
        processCapabilities(json);
    }

    /**
     * It processes the incoming URL and gets the response in the form of stream.
     * It then converts the stream into a string(it should be in json format)
     * @param 
     * 
     */
    public final void processCapabilities() throws IOException {
        processCapabilities(resourceToString("capabilities", null));
    }

    /**
     * This method processes the JSON from the server and extracts layer names
     * and populates typeNames
     * @param capabilitiesJSON
     * 
     */
    final void processCapabilities(String capabilitiesJSON) throws IOException {
        JSONParser parser = new JSONParser();

        try {
            Object obj = parser.parse(capabilitiesJSON);
            JSONArray array = (JSONArray) obj;

            Iterator itr = array.iterator();
            while (itr.hasNext()) {
                Map tmpMap = (HashMap) itr.next();
                String strName = ((String) tmpMap.get("name")).trim();
                Name name = new NameImpl(namespaceURI, strName);

                boolean xyOrder = (!tmpMap.containsKey("axisorder")) ? true
                        : (tmpMap.get("axisorder").equals("xy"));

                String strCRS = null;
                CoordinateReferenceSystem crs = null;
                if (tmpMap.containsKey("crs")) {
                    strCRS = ((String) tmpMap.get("crs")).trim();
                    crs = SFSDataStoreUtil.decodeXY(strCRS);
                }

                Envelope envelope = null;
                if (tmpMap.containsKey("bbox")) {
                    JSONArray boundingArray = (JSONArray) tmpMap.get("bbox");
                    if (!xyOrder) {
                        SFSDataStoreUtil.flipYXInsideTheBoundingBox(boundingArray);
                    }
                    envelope = new Envelope(((Number) boundingArray.get(0)).doubleValue(),
                            ((Number) boundingArray.get(2)).doubleValue(),
                            ((Number) boundingArray.get(1)).doubleValue(),
                            ((Number) boundingArray.get(3)).doubleValue());
                }

                SFSLayer layer = new SFSLayer(name, xyOrder, strCRS, crs, envelope);
                layers.put(name, layer);
            }
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Exception occurred while parsing the capabilities", e);
            throw (IOException) new IOException().initCause(e);
        }
    }

    /**
     * Return the Layer Names
     * @param 
     * @return typeNames
     * @throws IOException
     * 
     */
    public List<Name> createTypeNames() throws IOException {
        return new ArrayList<Name>(layers.keySet());
    }

    /**
     * Call FeatureSource
     * @param ContentEntry
     * @return ContentFeatureSource
     * @throws IOException
     * 
     */
    protected ContentFeatureSource createFeatureSource(ContentEntry entry) throws IOException {

        return new SFSFeatureSource(entry);
    }

    /**
     * Returns the layer definition for the given name
     * @param name
     * @return
     */
    SFSLayer getLayer(Name name) {
        return layers.get(name);
    }

    /**
     * This method requests the response from the URL either through GET/POST
     * and then converts the response to string and returns the string
     * fnData is URL encoded string
     * The request is going to be a POST one if postData is not null, a GET otherwise
     * @param is
     * @return String
     * @throws IOException
     */
    String resourceToString(String resource, String postData) throws IOException {
        InputStream is = resourceToStream(resource, postData);

        if (is != null) {
            StringBuilder sb = new StringBuilder();
            String line;
            try {
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(is, SFSDataStoreUtil.DEFAULT_ENCODING));
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } finally {
                is.close();
            }
            return sb.toString();
        }

        return "";
    }

    /**
     * This method requests the response from the URL either through GET/POST
     * and returns the response as a stream
     * The request is going to be a POST one if postData is not null, a GET otherwise
     * @param is
     * @return String
     * @throws IOException
     */
    InputStream resourceToStream(String resource, String postData)
            throws MalformedURLException, IOException, ProtocolException {
        boolean doPost = false;
        URL url;
        if (postData != null && (baseURL.length() + resource.length() + postData.length() < 2048)) {
            url = new URL(baseURL + resource + "?" + postData);
        } else {
            url = new URL(baseURL + resource);
        }

        // TODO: use commons http client + persistent connections instead
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestProperty("Accept-Encoding", "gzip");

        // if we have username/password use them to setup basic auth
        if (user != null && password != null) {
            String combined = nullToEmpty(user) + ":" + nullToEmpty(password);
            byte[] authBytes = combined.getBytes("US-ASCII");
            String encoded = new String(Base64.encodeBase64(authBytes));
            urlConnection.setRequestProperty("Authorization", "Basic " + encoded);
        }

        // enforce the timeout if we have one
        if (timeout > 0) {
            urlConnection.setConnectTimeout(timeout);
            urlConnection.setReadTimeout(timeout);
        }

        urlConnection.setDoInput(true);
        if (doPost && postData != null && !"".equals(postData.trim())) {
            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            urlConnection.setDoOutput(true);

            /*write the data to server*/
            OutputStreamWriter wr = null;
            try {
                wr = new OutputStreamWriter(urlConnection.getOutputStream());
                wr.write(postData);
                wr.flush();
            } finally {
                if (wr != null) {
                    wr.close();
                }
            }
        } else {
            urlConnection.setRequestMethod("GET");
        }

        /* Get the response from the server*/
        if (urlConnection.getResponseCode() != 200) {
            throw new IOException("Server reported and error with code " + urlConnection.getResponseCode() + ": "
                    + urlConnection.getResponseMessage() + " accessing resource " + url.toExternalForm());
        } else {
            InputStream is = urlConnection.getInputStream();
            String encoding = urlConnection.getContentEncoding();
            if ("gzip".equalsIgnoreCase(encoding)) {
                is = new GZIPInputStream(is);
            }

            return is;
        }
    }

    private String nullToEmpty(String string) {
        return string != null ? string : "";
    }

}