org.neo4j.gis.spatial.osm.OSMLayer.java Source code

Java tutorial

Introduction

Here is the source code for org.neo4j.gis.spatial.osm.OSMLayer.java

Source

/**
 * Copyright (c) 2010-2013 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.neo4j.gis.spatial.osm;

import java.io.File;
import java.util.HashMap;

import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.json.simple.JSONObject;
import org.neo4j.gis.spatial.rtree.NullListener;
import org.neo4j.gis.spatial.Constants;
import org.neo4j.gis.spatial.DynamicLayer;
import org.neo4j.gis.spatial.DynamicLayerConfig;
import org.neo4j.gis.spatial.SpatialDatabaseService;
import org.neo4j.gis.spatial.SpatialDataset;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
 * Instances of this class represent the primary layer of the OSM Dataset. It
 * extends the DynamicLayer class becauase the OSM dataset can have many layers.
 * Only one is primary, the layer containing all ways. Other layers are dynamic.
 * 
 * @author craig
 * @since 1.0.0
 */
public class OSMLayer extends DynamicLayer {
    private OSMDataset osmDataset;

    public SpatialDataset getDataset() {
        if (osmDataset == null) {
            osmDataset = new OSMDataset(getSpatialDatabase(), this, layerNode);
        }
        return osmDataset;
    }

    /**
     * This method is used to find or construct the necessary dataset object on
     * an existing dataset node and layer. This will create the relationships
     * between the two if it is missing.
     */
    public OSMDataset getDataset(long datasetId) {
        if (osmDataset == null) {
            osmDataset = new OSMDataset(this.getSpatialDatabase(), this, layerNode, datasetId);
        }
        return osmDataset;
    }

    public Integer getGeometryType() {
        // The core layer in OSM is based on the Ways, and we return all of them
        // as LINESTRING and POLYGON, so we use the parent GEOMETRY
        return GTYPE_GEOMETRY;
    }

    /**
     * OSM always uses WGS84 CRS; so we return that.
     */
    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
        try {
            return DefaultGeographicCRS.WGS84;
        } catch (Exception e) {
            System.err.println("Failed to decode WGS84 CRS: " + e.getMessage());
            e.printStackTrace(System.err);
            return null;
        }
    }

    protected void clear() {
        index.clear(new NullListener());
    }

    public Node addWay(Node way) {
        return addWay(way, false);
    }

    public Node addWay(Node way, boolean verifyGeom) {
        Relationship geomRel = way.getSingleRelationship(OSMRelation.GEOM, Direction.OUTGOING);
        if (geomRel != null) {
            Node geomNode = geomRel.getEndNode();
            try {
                // This is a test of the validity of the geometry, throws exception on error
                if (verifyGeom)
                    getGeometryEncoder().decodeGeometry(geomNode);
                index.add(geomNode);
            } catch (Exception e) {
                System.err.println("Failed geometry test on node "
                        + geomNode.getProperty("name", geomNode.toString()) + ": " + e.getMessage());
                for (String key : geomNode.getPropertyKeys()) {
                    System.err.println("\t" + key + ": " + geomNode.getProperty(key));
                }
                System.err.println("For way node " + way);
                for (String key : way.getPropertyKeys()) {
                    System.err.println("\t" + key + ": " + way.getProperty(key));
                }
                // e.printStackTrace(System.err);
            }
            return geomNode;
        } else {
            return null;
        }
    }

    /**
      * Provides a method for iterating over all nodes that represent geometries in this layer.
      * This is similar to the getAllNodes() methods from GraphDatabaseService but will only return
      * nodes that this dataset considers its own, and can be passed to the GeometryEncoder to
      * generate a Geometry. There is no restricting on a node belonging to multiple datasets, or
      * multiple layers within the same dataset.
      * 
      * @return iterable over geometry nodes in the dataset
      */
    public Iterable<Node> getAllGeometryNodes() {
        return index.getAllIndexedNodes();
    }

    public boolean removeDynamicLayer(String name) {
        return removeLayerConfig(name);
    }

    @SuppressWarnings("unchecked")
    /**
     * <pre>
     * { "step": {"type": "GEOM", "direction": "INCOMING"
     *     "step": {"type": "TAGS", "direction": "OUTGOING"
     *       "properties": {"highway": "residential"}
     *     }
     *   }
     * }
     * </pre>
     * 
     * This will work with OSM datasets, traversing from the geometry node
     * to the way node and then to the tags node to test if the way is a
     * residential street.
     */
    public DynamicLayerConfig addDynamicLayerOnWayTags(String name, int type, HashMap<?, ?> tags) {
        JSONObject query = new JSONObject();
        if (tags != null && !tags.isEmpty()) {
            JSONObject step2tags = new JSONObject();
            JSONObject step2way = new JSONObject();
            JSONObject properties = new JSONObject();
            for (Object key : tags.keySet()) {
                Object value = tags.get(key);
                if (value != null && (value.toString().length() < 1 || value.equals("*")))
                    value = null;
                properties.put(key.toString(), value);
            }

            step2tags.put("properties", properties);
            step2tags.put("type", "TAGS");
            step2tags.put("direction", "OUTGOING");

            step2way.put("step", step2tags);
            step2way.put("type", "GEOM");
            step2way.put("direction", "INCOMING");

            query.put("step", step2way);
        }
        if (type > 0) {
            JSONObject properties = new JSONObject();
            properties.put(PROP_TYPE, type);
            query.put("properties", properties);
        }
        System.out.println("Created dynamic layer query: " + query.toJSONString());
        return addLayerConfig(name, type, query.toJSONString());
    }

    /**
     * Add a rule for a pure way based search, with a single property key/value
     * match on the way tags. All ways with the specified tag property will be
     * returned. This convenience method will automatically name the layer based
     * on the key/value passed, namely 'key-value'. If you want more control
     * over the naming, revert to the addDynamicLayerOnWayTags method.
     * The geometry is assumed to be LineString, the most common type for ways.
     * 
     * @param key
     * @param value
     */
    public DynamicLayerConfig addSimpleDynamicLayer(String key, String value) {
        return addSimpleDynamicLayer(key, value, Constants.GTYPE_LINESTRING);
    }

    /**
     * Add a rule for a pure way based search, with a single property key/value
     * match on the way tags. All ways with the specified tag property will be
     * returned. This convenience method will automatically name the layer based
     * on the key/value passed, namely 'key-value'. If you want more control
     * over the naming, revert to the addDynamicLayerOnWayTags method.
     * 
     * @param key
     * @param value
     * @param geometry type as defined in Constants.
     */
    public DynamicLayerConfig addSimpleDynamicLayer(String key, String value, int gtype) {
        HashMap<String, String> tags = new HashMap<String, String>();
        tags.put(key, value);
        return addDynamicLayerOnWayTags(value == null ? key : key + "-" + value, gtype, tags);
    }

    /**
     * Add a rule for a pure way based search, with multiple property key/value
     * match on the way tags. All ways with the specified tag properties will be
     * returned. This convenience method will automatically name the layer based
     * on the key/value pairs passed, namely 'key-value-key-value-...'. If you
     * want more control over the naming, revert to the addDynamicLayerOnWayTags
     * method.
     * 
     * @param geometry
     *            type as defined in Constants.
     * @param query
     *            String of ',' separated key=value tags to match
     */
    public DynamicLayerConfig addSimpleDynamicLayer(int gtype, String tagsQuery) {
        HashMap<String, String> tags = new HashMap<String, String>();
        StringBuffer name = new StringBuffer();
        for (String query : tagsQuery.split("\\s*\\,\\s*")) {
            String[] fields = query.split("\\s*\\=+\\s*");
            String key = fields[0];
            String value = fields.length > 1 ? fields[1] : "*";
            tags.put(key, value);
            if (name.length() > 0)
                name.append("-");
            name.append(key);
            name.append("-");
            if (!value.equals("*"))
                name.append(value);
        }
        return addDynamicLayerOnWayTags(name.toString(), gtype, tags);
    }

    /**
     * Add a rule for a pure way based search, with multiple property key/value
     * match on the way tags. All ways with the specified tag properties will be
     * returned. This convenience method will automatically name the layer based
     * on the key/value pairs passed, namely 'key-value-key-value-...'. If you
     * want more control over the naming, revert to the addDynamicLayerOnWayTags
     * method. The geometry type will be assumed to be LineString.
     * 
     * @param query
     *            String of ',' separated key=value tags to match
     */
    public DynamicLayerConfig addSimpleDynamicLayer(String tagsQuery) {
        return addSimpleDynamicLayer(GTYPE_LINESTRING, tagsQuery);
    }

    /**
     * Add a rule for a pure way based search, with a check on geometry type only.
     * 
     * @param geometry type as defined in Constants.
     */
    public DynamicLayerConfig addSimpleDynamicLayer(int gtype) {
        return addDynamicLayerOnWayTags(SpatialDatabaseService.convertGeometryTypeToName(gtype), gtype, null);
    }

    /**
     * The OSM dataset has a number of possible stylesOverride this method to provide a style if your layer wishes to control
     * its own rendering in the GIS.
     * 
     * @return Style or null
     */
    public File getStyle() {
        return new File("dev/neo4j/neo4j-spatial/src/main/resources/sld/osm/osm.sld");
    }

}