/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2015, Open Source Geospatial Foundation (OSGeo)
* (C) 2014-2015, Boundless
*
* 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.mongodb;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.vividsolutions.jts.geom.Geometry;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.data.mongodb.complex.MongoComplexUtilities;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.Name;
/**
*
* @author tkunicki@boundlessgeo.com
*
*/
public class MongoInferredMapper extends AbstractCollectionMapper {
public final static Logger LOG = Logging.getLogger(MongoInferredMapper.class);
MongoGeometryBuilder geomBuilder = new MongoGeometryBuilder();
SimpleFeatureType schema;
@Override
public String getGeometryPath() {
String gdName = schema.getGeometryDescriptor().getLocalName();
return (String)schema.getDescriptor(gdName).getUserData().get(MongoDataStore.KEY_mapping);
}
@Override
public String getPropertyPath(String property) {
AttributeDescriptor descriptor = schema.getDescriptor(property);
return descriptor == null ? null :
(String)descriptor.getUserData().get(MongoDataStore.KEY_mapping);
}
@Override
public Geometry getGeometry(DBObject dbo) {
Object o = MongoUtil.getDBOValue(dbo, getGeometryPath());
// TODO legacy coordinate pair
return o == null ? null : geomBuilder.toGeometry((DBObject)o);
}
@Override
public DBObject toObject(Geometry g) {
return geomBuilder.toObject(g);
}
@Override
public void setGeometry(DBObject dbo, Geometry g) {
MongoUtil.setDBOValue(dbo, getGeometryPath(), toObject(g));
}
@Override
public SimpleFeatureType buildFeatureType(Name name, DBCollection collection) {
Set<String> indexedGeometries = MongoUtil.findIndexedGeometries(collection);
Set<String> indexedFields = MongoUtil.findIndexedFields(collection);
//Map<String, Class<?>> mappedFields = MongoUtil.findMappableFields(collection);
Map<String, Class> mappedFields = MongoComplexUtilities.findMappings(collection.findOne());
// don't need to worry about indexed properties we've found in our scan...
indexedFields.removeAll(mappedFields.keySet());
// remove geometries from indexed and mapped sets
indexedFields.removeAll(indexedGeometries);
for (String mappedProperty : new ArrayList<String>(mappedFields.keySet())) {
for (String indexedGeometry : indexedGeometries) {
if (mappedProperty.startsWith(indexedGeometry)) {
mappedFields.remove(mappedProperty);
break;
}
}
}
//Examine the DBO and remove any invalid indexed fields (such as arrays)
DBObject dbo = collection.findOne();
if (dbo != null) {
Iterator<String> indexedIterator = indexedFields.iterator();
while (indexedIterator.hasNext()) {
Object value = MongoUtil.getDBOValue(dbo, indexedIterator.next());
if (value == null) {
indexedIterator.remove();
}
}
}
SimpleFeatureTypeBuilder ftBuilder = new SimpleFeatureTypeBuilder();
ftBuilder.setName(name);
// NOTE: for now we just use first (hopefully only) indexed geometry we find
String geometryField = indexedGeometries.iterator().next();
if (indexedGeometries.size() > 1) {
LOG.log(Level.WARNING, "More than one indexed geometry field found for type {0}, selecting {1} (first one encountered with index search of collection {2})",
new Object[] {name, geometryField, collection.getFullName() });
}
ftBuilder.userData(MongoDataStore.KEY_mapping, geometryField);
ftBuilder.userData(MongoDataStore.KEY_encoding, "GeoJSON");
ftBuilder.add(geometryField, Geometry.class, DefaultGeographicCRS.WGS84);
LOG.log(Level.INFO, "building type {0}: mapping geometry field {1} from collection {2}",
new Object[] {name, geometryField, collection.getFullName() });
for (Map.Entry<String, Class> mappedField : mappedFields.entrySet()) {
String field = mappedField.getKey();
Class<?> binding = mappedField.getValue();
ftBuilder.userData(MongoDataStore.KEY_mapping, field);
ftBuilder.add(field, binding);
LOG.log(Level.INFO, "building type \"{0}\": mapping field \"{1}\" with binding {2} from collection {3}",
new Object[] {name, field, binding.getName(), collection.getFullName() });
}
for (String field : indexedFields) {
ftBuilder.userData(MongoDataStore.KEY_mapping, field);
ftBuilder.add(field, String.class);
LOG.log(Level.INFO, "building type \"{0}\": mapping indexed field \"{1}\" with default binding, {2}, from collection {3}",
new Object[] {name, field, String.class.getName(), collection.getFullName() });
}
SimpleFeatureType featureType = ftBuilder.buildFeatureType();
featureType.getUserData().put(MongoDataStore.KEY_collection, collection.getName());
this.schema = featureType;
return featureType;
}
}