/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, 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.kml.bindings; import java.net.URI; import java.util.AbstractMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.xml.namespace.QName; import org.geotools.feature.FeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.kml.FolderStack; import org.geotools.kml.KML; import org.geotools.kml.StyleMap; import org.geotools.kml.v22.SchemaRegistry; import org.geotools.styling.FeatureTypeStyle; import org.geotools.xml.AbstractComplexBinding; import org.geotools.xml.ElementInstance; import org.geotools.xml.Node; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.Name; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Point; /** * Binding object for the type http://earth.google.com/kml/2.1:FeatureType. * * <p> * <pre> * <code> * <complexType abstract="true" name="FeatureType"> * <complexContent> * <extension base="kml:ObjectType"> * <sequence> * <element minOccurs="0" name="name" type="string"/> * <element default="1" minOccurs="0" name="visibility" type="boolean"/> * <element default="1" minOccurs="0" name="open" type="boolean"/> * <element minOccurs="0" name="address" type="string"/> * <element minOccurs="0" name="phoneNumber" type="string"/> * <element minOccurs="0" name="Snippet" type="kml:SnippetType"/> * <element minOccurs="0" name="description" type="string"/> * <element minOccurs="0" ref="kml:LookAt"/> * <element minOccurs="0" ref="kml:TimePrimitive"/> * <element minOccurs="0" ref="kml:styleUrl"/> * <element maxOccurs="unbounded" minOccurs="0" ref="kml:StyleSelector"/> * <element minOccurs="0" ref="kml:Region"/> * <element minOccurs="0" name="Metadata" type="kml:MetadataType"/> * </sequence> * </extension> * </complexContent> * </complexType> * * </code> * </pre> * </p> * * @generated * * * * @source $URL$ */ public class FeatureTypeBinding extends AbstractComplexBinding { /** * base feature type for kml features, used when no Schema element is specified */ protected static final SimpleFeatureType FeatureType; StyleMap styleMap; private final FolderStack folderStack; private SchemaRegistry schemaRegistry; static { SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.setNamespaceURI(KML.NAMESPACE); tb.setName("feature"); //<element minOccurs="0" name="name" type="string"/> tb.add("name", String.class); //<element default="1" minOccurs="0" name="visibility" type="boolean"/> tb.add("visibility", Boolean.class); //<element default="1" minOccurs="0" name="open" type="boolean"/> tb.add("open", Boolean.class); //<element minOccurs="0" name="address" type="string"/> tb.add("address", String.class); //<element minOccurs="0" name="phoneNumber" type="string"/> tb.add("phoneNumber", String.class); //<element minOccurs="0" name="Snippet" type="kml:SnippetType"/> //tb.add("Snippet",String.class): //<element minOccurs="0" name="description" type="string"/> tb.add("description", String.class); //<element minOccurs="0" ref="kml:LookAt"/> tb.add("LookAt", Point.class); //<element minOccurs="0" ref="kml:TimePrimitive"/> //tb.add("TimePrimitive", ...); //<element minOccurs="0" ref="kml:styleUrl"/> tb.add("Style", FeatureTypeStyle.class); //<element maxOccurs="unbounded" minOccurs="0" ref="kml:StyleSelector"/> //<element minOccurs="0" ref="kml:Region"/> tb.add("Region", LinearRing.class); FeatureType = tb.buildFeatureType(); } public FeatureTypeBinding(StyleMap styleMap, FolderStack folderStack, SchemaRegistry schemaRegistry) { this.styleMap = styleMap; this.folderStack = folderStack; this.schemaRegistry = schemaRegistry; } /** * @generated */ public QName getTarget() { return KML.FeatureType; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * * @generated modifiable */ public Class getType() { return SimpleFeature.class; } private SimpleFeatureType appendAttributes(SimpleFeatureType acc, SimpleFeatureType typeToAppend) { if (typeToAppend == null) { return acc; } SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.init(acc); for (AttributeDescriptor ad : typeToAppend.getAttributeDescriptors()) { // only add attributes that we don't already have if (acc.getDescriptor(ad.getLocalName()) == null) { tb.add(ad); } } return tb.buildFeatureType(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * * @generated modifiable */ public Object parse(ElementInstance instance, Node node, Object value) throws Exception { // start off with the default feature type, and retype as necessary SimpleFeatureType featureType = FeatureType; // retype based on schema if we have extended data pointing to a url @SuppressWarnings("unchecked") Map<String, Object> extData = (Map<String, Object>) node.getChildValue("ExtendedData"); if (extData != null) { @SuppressWarnings("unchecked") List<URI> schemaURI = (List<URI>) extData.get("schemas"); if (schemaURI != null) { for (URI uri : schemaURI) { String normalizedSchemaName = normalizeSchemaName(uri); SimpleFeatureType schemaType = schemaRegistry.get(normalizedSchemaName); featureType = appendAttributes(featureType, schemaType); } } } // if we are a custom schema element, add the attributes to the type SimpleFeatureType customFeatureType = schemaRegistry.get(instance.getName()); featureType = appendAttributes(featureType, customFeatureType); SimpleFeatureBuilder b = new SimpleFeatureBuilder(featureType); //<element minOccurs="0" name="name" type="string"/> b.set("name", node.getChildValue("name")); //<element default="1" minOccurs="0" name="visibility" type="boolean"/> b.set("visibility", node.getChildValue("visibility", Boolean.TRUE)); //<element default="1" minOccurs="0" name="open" type="boolean"/> b.set("open", node.getChildValue("open", Boolean.TRUE)); //<element minOccurs="0" name="address" type="string"/> b.set("address", node.getChildValue("address")); //<element minOccurs="0" name="phoneNumber" type="string"/> b.set("phoneNumber", node.getChildValue("phoneNumber")); //<element minOccurs="0" name="Snippet" type="kml:SnippetType"/> //tb.add("Snippet",String.class): //<element minOccurs="0" name="description" type="string"/> b.set("description", node.getChildValue("description")); //<element minOccurs="0" ref="kml:LookAt"/> b.set("LookAt", node.getChildValue("LookAt")); //<element minOccurs="0" ref="kml:TimePrimitive"/> //tb.add("TimePrimitive", ...); //<element minOccurs="0" ref="kml:styleUrl"/> URI uri = (URI) node.getChildValue("styleUrl"); if (uri != null) { //load the style from the style map //TODO: use a proxy to do forward referencing b.set("Style", styleMap.get(uri)); } //<element maxOccurs="unbounded" minOccurs="0" ref="kml:StyleSelector"/> //<element minOccurs="0" ref="kml:Region"/> b.set("Region", node.getChildValue("Region")); // stick extended data in feature user data if (extData != null) { b.featureUserData("UntypedExtendedData", extData.get("untyped")); @SuppressWarnings("unchecked") Map<String, Object> typedUserData = (Map<String, Object>) extData.get("typed"); if (typedUserData != null) { for (Entry<String, Object> entry : typedUserData.entrySet()) { String attrName = entry.getKey(); if (featureType.getDescriptor(attrName) != null) { b.set(attrName, entry.getValue()); } } } } // if we are a custom schema type // add in any attributes from that type onto the feature if (customFeatureType != null) { for (AttributeDescriptor ad : customFeatureType.getAttributeDescriptors()) { String attrName = ad.getLocalName(); Object childValue = node.getChildValue(attrName); if (childValue != null) { b.set(attrName, childValue); } } } // stick folder stack in feature user data b.featureUserData("Folder", folderStack.asList()); //<element minOccurs="0" name="Metadata" type="kml:MetadataType"/> return b.buildFeature((String) node.getAttributeValue("id")); } private String normalizeSchemaName(URI schemaURI) { if (schemaURI.getFragment() != null) { return schemaURI.getFragment(); } return schemaURI.getPath(); } public Object getProperty(Object object, QName name) throws Exception { if( object instanceof FeatureCollection){ FeatureCollection features = (FeatureCollection) object; if ( "id".equals( name.getLocalPart() ) ) { return features.getID(); } } if( object instanceof SimpleFeature){ SimpleFeature feature = (SimpleFeature) object; if ( "id".equals( name.getLocalPart() ) ) { return feature.getID(); } //<element minOccurs="0" name="name" type="string"/> if ( "name".equals( name.getLocalPart() ) ) { return feature.getAttribute( "name" ); } //<element minOccurs="0" name="description" type="string"/> if ( "description".equals( name.getLocalPart() ) ) { return feature.getAttribute( "description" ); } if (KML.styleUrl.getLocalPart().equals(name.getLocalPart())) { URI uri = (URI) feature.getAttribute("Style"); if (uri != null) { return styleMap.get(uri); } } //<element default="1" minOccurs="0" name="visibility" type="boolean"/> //<element default="1" minOccurs="0" name="open" type="boolean"/> //<element minOccurs="0" name="address" type="string"/> //<element minOccurs="0" name="phoneNumber" type="string"/> //<element minOccurs="0" name="Snippet" type="kml:SnippetType"/> //<element minOccurs="0" name="description" type="string"/> //<element minOccurs="0" ref="kml:LookAt"/> //<element minOccurs="0" ref="kml:TimePrimitive"/> //<element minOccurs="0" ref="kml:styleUrl"/> //<element maxOccurs="unbounded" minOccurs="0" ref="kml:StyleSelector"/> //<element minOccurs="0" ref="kml:Region"/> //<element minOccurs="0" name="Metadata" type="kml:MetadataType"/> // this is KML 2.2-specific if ("ExtendedData".equals(name.getLocalPart())) { SimpleFeatureType t = feature.getFeatureType(); List<Map.Entry<Name, Object>> attributes = new LinkedList<Map.Entry<Name, Object>>(); for (AttributeDescriptor ad : t.getAttributeDescriptors()) { Object obj = feature.getAttribute(ad.getName()); // do not include geographic attributes if (!(obj instanceof Geometry)) attributes .add(new AbstractMap.SimpleEntry<Name, Object>(ad.getName(), obj)); } return attributes; } } return super.getProperty(object, name); } }