package org.geoserver.csw.records.iso;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.opengis.cat.csw20.ElementSetType;
import org.geoserver.csw.records.AbstractRecordDescriptor;
import org.geoserver.csw.records.CSWRecordDescriptor;
import org.geoserver.csw.records.RecordFeatureTypeRegistryConfiguration;
import org.geoserver.csw.records.SpatialFilterChecker;
import org.geoserver.platform.GeoServerExtensions;
import org.geotools.csw.CSW;
import org.geotools.data.Query;
import org.geotools.data.complex.config.EmfComplexFeatureReader;
import org.geotools.data.complex.config.FeatureTypeRegistry;
import org.geotools.feature.NameImpl;
import org.geotools.feature.TypeBuilder;
import org.geotools.feature.type.FeatureTypeFactoryImpl;
import org.geotools.filter.SortByImpl;
import org.geotools.xml.SchemaIndex;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.FeatureTypeFactory;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.sort.SortBy;
import org.xml.sax.helpers.NamespaceSupport;
import com.vividsolutions.jts.geom.MultiPolygon;
/**
* Describes the ISO MetaData records and provides some handy constants to help building features
* representing MD_Metadata.
*
* @author Niels Charlier
*
*/
public class MetaDataDescriptor extends AbstractRecordDescriptor {
public static final NamespaceSupport NAMESPACES;
public static final String NAMESPACE_GCO = "http://www.isotc211.org/2005/gco";
public static final String NAMESPACE_GMD = "http://www.isotc211.org/2005/gmd";
public static final String NAMESPACE_APISO = "http://www.opengis.net/cat/csw/apiso/1.0";
public static FeatureType METADATA_TYPE;
public static AttributeDescriptor METADATA_DESCRIPTOR;
public static final NameImpl RECORD_BBOX_NAME = new NameImpl(NAMESPACE_GMD, "EX_GeographicBoundingBox");
public static final List<Name> BRIEF_ELEMENTS;
public static final List<Name> SUMMARY_ELEMENTS;
public static final List<Name> QUERYABLES;
public static Map<String, PropertyName> QUERYABLE_MAPPING = new HashMap<String, PropertyName>();
static {
// prepare the common namespace support
NAMESPACES = new NamespaceSupport();
NAMESPACES.declarePrefix("", NAMESPACE_APISO);
NAMESPACES.declarePrefix("csw", CSW.NAMESPACE);
NAMESPACES.declarePrefix("gco", NAMESPACE_GCO);
NAMESPACES.declarePrefix("gmd", NAMESPACE_GMD);
FeatureTypeFactory typeFactory = new FeatureTypeFactoryImpl();
EmfComplexFeatureReader reader = EmfComplexFeatureReader.newInstance();
SchemaIndex index = null;
try {
index = reader.parse(new URL("http://schemas.opengis.net/iso/19139/20070417/gmd/metadataEntity.xsd"));
} catch (IOException e) {
//this is fatal
throw new RuntimeException(e);
}
FeatureTypeRegistry featureTypeRegistry = new FeatureTypeRegistry(NAMESPACES, typeFactory, new RecordFeatureTypeRegistryConfiguration("MD_Metadata_Type"), true);
TypeBuilder builder = new TypeBuilder(typeFactory);
builder.setNamespaceURI(NAMESPACE_GMD);
builder.setName("EX_GeographicBoundingBox_Type");
builder.setBinding(MultiPolygon.class);
builder.crs(CSWRecordDescriptor.DEFAULT_CRS);
AttributeType bboxType = builder.geometry();
featureTypeRegistry.register(bboxType);
featureTypeRegistry.addSchemas(index);
METADATA_TYPE = (FeatureType) featureTypeRegistry.getAttributeType(new NameImpl(
NAMESPACE_GMD, "MD_Metadata_Type"));
METADATA_DESCRIPTOR = featureTypeRegistry.getDescriptor(new NameImpl(
NAMESPACE_GMD, "MD_Metadata"), null);
BRIEF_ELEMENTS = createNameList(NAMESPACES, "Identifier", "Title", "Type", "BoundingBox", "GraphicOverview", "ServiceType", "ServiceTypeVersion");
SUMMARY_ELEMENTS = createNameList(NAMESPACES, "Abstract", "CharacterSet", "Creator", "Contributor", "CouplingType", "BoundingBox", "Format", "FormatVersion", "GraphicOverview", "HierarchyLevelName", "Identifier", "Language", "Lineage", "MetadataCharacterSet", "MetadataStandardName", "MetadataStandardVersion", "Modified", "OnlineResource", "ParentIdentifier", "Publisher", "ResourceLanguage", "ReferenceSystem", "RevisionDate", "Rights", "ServiceOperation", "ServiceType", "ServiceTypeVersion", "SpatialResolution", "SpatialRepresentationType", "Title", "TopicCategory", "Type");
QUERYABLES = SUMMARY_ELEMENTS;
//brief
addQueryableMapping("Identifier","gmd:fileIdentifier/gco:CharacterString");
addQueryableMapping("Title","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:citation/gmd:CI_Citation/gmd:title/gco:CharacterString");
addQueryableMapping("Type","gmd:hierarchyLevel/gmd:MD_ScopeCode/@codeListValue");
addQueryableMapping("BoundingBox","gmd:identificationInfo/gmd:MD_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox");
addQueryableMapping("GraphicOverview","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:graphicOverview/gmd:MD_BrowseGraphic/gmd:fileName/gco:CharacterString");
addQueryableMapping("ServiceType","gmd:identificationInfo/gmd:SV_ServiceIdentification/gmd:serviceType/gco:CharacterString");
addQueryableMapping("ServiceTypeVersion","gmd:identificationInfo/gmd:SV_ServiceIdentification/gmd:serviceTypeVersion/gco:CharacterString");
//:summary
addQueryableMapping("Abstract","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:abstract/gco:CharacterString");
addQueryableMapping("CharacterSet","gmd:identificationInfo/gmd:MD_DataIdentification/gmd:characterSet/gmd:MD_CharacterSetCode/@codeListValue");
addQueryableMapping("Creator","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName[role/gmd:CI_RoleCode/gmd:@codeListValue=’originator’]/gco:CharacterString");
addQueryableMapping("Contributor","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName[role/gmd:CI_RoleCode/gmd:@codeListValue=’author’]/gco:CharacterString");
addQueryableMapping("CouplingType","gmd:identificationInfo/gmd:SV_ServiceIdentification/gmd:couplingType/gmd:SV_CouplingType/gmd:code/@codeListValue");
addQueryableMapping("Format","gmd:distributionInfo/gmd:MD_Distribution/gmd:distributionFormat/gmd:MD_Format/gmd:name/gco:CharacterString");
addQueryableMapping("FormatVersion","gmd:distributionInfo/gmd:MD_Distribution/gmd:distributionFormat/gmd:MD_Format/gmd:version/gco:CharacterString");
addQueryableMapping("HierarchyLevelName","gmd:hierarchyLevelName/gco:CharacterString");
addQueryableMapping("Language","gmd:language/gco:CharacterString");
addQueryableMapping("Lineage","gmd:dataQualityInfo/gmd:DQ_DataQuality/gmd:lineage/gmd:LI_Lineage/gmd:statement/gco:CharacterString");
addQueryableMapping("MetadataCharacterSet","gmd:characterSet/gmd:MD_ScopeCode/@codeListValue");
addQueryableMapping("MetadataStandardName","gmd:metadataStandardName/gco:CharacterString");
addQueryableMapping("MetadataStandardVersion","gmd:metadataStandardVersion/gco:CharacterString");
addQueryableMapping("Modified","gmd:dateStamp/gmd:Date");
addQueryableMapping("OnlineResource","gmd:distributionInfo/gmd:MD_Distribution/gmd:transferOptions/gmd:MD_DigitalTransferOption/gmd:onLine/gmd:CI_OnlineResource/gmd:linkage/gmd:URL/gco:CharacterString");
addQueryableMapping("ParentIdentifier","gmd:parentIdentifier/gco:CharacterString");
addQueryableMapping("Publisher","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName[role/gmd:CI_RoleCode/gmd:@codeListValue=’publisher’]/gco:CharacterString");
addQueryableMapping("ResourceLanguage","gmd:identificationInfo/gmd:MD_DataIdentification/gmd:language/gco:CharacterString");
addQueryableMapping("ReferenceSystem","gmd:referenceSystemInfo/gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gco:CharacterString");
addQueryableMapping("RevisionDate","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:citation/gmd:CI_Citation/gmd:date/gmd:CI_Date[dateType/gmd:CI_DateTypeCode/gmd:@codeListValue='revision']/gmd:date/gmd:Date");
addQueryableMapping("Rights","gmd:identificationInfo/gmd:AbstractMD_Identification/gmd:resourceConstraints/gmd:MD_LegalConstraints/gmd:accessConstraints/@codeListValue");
addQueryableMapping("ServiceOperation","gmd:identificationInfo/gmd:SV_ServiceIdentification/gmd:containsOperations/gmd:SV_OperationMetadata/gco:CharacterString");
addQueryableMapping("SpatialResolution","gmd:identificationInfo/gmd:MD_DataIdentification/gmd:spatialResolution/gco:CharacterString");
addQueryableMapping("SpatialRepresentationType","gmd:identificationInfo/gmd:MD_DataIdentification/gmd:spatialRepresentationType/gmd:MD_SpatialRepresentationTypeCode/@codeListValue");
addQueryableMapping("TopicCategory","gmd:identificationInfo/gmd:MD_DataIdentification/gmd:topicCategory/gco:CharacterString");
//required
addQueryableMapping("Contact", "gmd:contact/gmd:CI_ResponsibleParty/gmd:individualName/gco:CharacterString");
}
/**
* Helper method to add Mapping in to Queryables
*
* @param key
* @param path
*/
protected static void addQueryableMapping(String key, String path) {
QUERYABLE_MAPPING.put(key, ff.property(path, NAMESPACES));
}
@Override
public String getOutputSchema() {
return NAMESPACE_GMD;
}
@Override
public List<Name> getPropertiesForElementSet(ElementSetType elementSet) {
switch (elementSet) {
case BRIEF:
return MetaDataDescriptor.BRIEF_ELEMENTS;
case SUMMARY:
return MetaDataDescriptor.SUMMARY_ELEMENTS;
default:
return null;
}
}
@Override
public NamespaceSupport getNamespaceSupport() {
return NAMESPACES;
}
@Override
public Query adaptQuery(Query query) {
Filter filter = query.getFilter();
if(filter != null && !Filter.INCLUDE.equals(filter)) {
query.setFilter ((Filter) filter.accept(new MDQueryableFilterVisitor(), null));
}
SortBy[] sortBy = query.getSortBy();
if(sortBy != null && sortBy.length > 0) {
for (int i = 0; i < sortBy.length; i++) {
SortBy sb = sortBy[i];
if(!SortBy.NATURAL_ORDER.equals(sb) && !SortBy.REVERSE_ORDER.equals(sb)) {
sortBy[i] = new SortByImpl(MDQueryableFilterVisitor.property(sb.getPropertyName()), sb.getSortOrder());
}
}
query.setSortBy(sortBy);
}
return query;
}
@Override
public AttributeDescriptor getFeatureDescriptor() {
return METADATA_DESCRIPTOR;
}
@Override
public String getBoundingBoxPropertyName() {
return "identificationInfo.MD_DataIdentification.extent.EX_Extent.geographicElement.EX_GeographicBoundingBox";
}
@Override
public List<Name> getQueryables() {
return QUERYABLES;
}
@Override
public String getQueryablesDescription() {
return "SupportedISOQueryables";
}
@Override
public PropertyName translateProperty(Name name) {
return QUERYABLE_MAPPING.get(name.getLocalPart());
}
public void verifySpatialFilters(Filter filter) {
filter.accept(new SpatialFilterChecker(getFeatureType()), null);
}
//singleton
private MetaDataDescriptor(){ }
private static MetaDataDescriptor INSTANCE;
public static MetaDataDescriptor getInstance() {
if (INSTANCE == null) {
INSTANCE = GeoServerExtensions.bean(MetaDataDescriptor.class);
if (INSTANCE == null) {
INSTANCE = new MetaDataDescriptor();
}
}
return INSTANCE;
}
}