/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013, Geomatys
*
* 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.geotoolkit.data.mapinfo.mif;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.data.*;
import org.geotoolkit.data.mapinfo.ProjectionUtils;
import org.geotoolkit.data.query.DefaultQueryCapabilities;
import org.geotoolkit.data.query.Query;
import org.geotoolkit.data.query.QueryCapabilities;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.parameter.Parameters;
import org.opengis.util.GenericName;
import org.opengis.filter.Filter;
import org.opengis.filter.identity.FeatureId;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.sis.feature.ReprojectFeatureType;
import org.apache.sis.storage.IllegalNameException;
import org.geotoolkit.data.memory.GenericDecoratedFeatureIterator;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.storage.DataStores;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureType;
/**
* A featureStore for MapInfo exchange format MIF-MID.
*
* @author Alexis Manin (Geomatys)
* Date : 21/02/13
*/
public class MIFFeatureStore extends AbstractFeatureStore {
private final MIFManager manager;
private final QueryCapabilities queryCapabilities = new DefaultQueryCapabilities(false);
/**
* Creates a new instance of MIFFeatureStore.
*
* @param uri The URL of the MIF file to use for this DataStore.
*
* @throws DataStoreException If we got a problem getting needed files.
*/
public MIFFeatureStore(final URI uri) throws DataStoreException {
this(uri, null);
}
/**
* This sets the datastore's namespace during construction (so the schema -
* FeatureType - will have the correct value) You can call this with
* namespace = null, but I suggest you give it an actual namespace.
*
* @param uri
* @param namespace
*/
public MIFFeatureStore(final URI uri, final String namespace) throws DataStoreException {
this(toParameter(uri, namespace));
}
public MIFFeatureStore(final ParameterValueGroup params) throws DataStoreException {
super(params);
final URI filePath = (URI) params.parameter(MIFFeatureStoreFactory.PATH.getName().toString()).getValue();
try {
manager = new MIFManager(filePath);
} catch (Exception e) {
throw new DataStoreException("Datastore can't reach target data.", e);
}
}
private static ParameterValueGroup toParameter(final URI uri, final String namespace) {
final ParameterValueGroup params = MIFFeatureStoreFactory.PARAMETERS_DESCRIPTOR.createValue();
Parameters.getOrCreate(MIFFeatureStoreFactory.PATH, params).setValue(uri);
Parameters.getOrCreate(MIFFeatureStoreFactory.NAMESPACE, params).setValue(namespace);
return params;
}
/**
* {@inheritDoc}
*/
@Override
public FeatureStoreFactory getFactory() {
return (FeatureStoreFactory) DataStores.getFactoryById(MIFFeatureStoreFactory.NAME);
}
/**
* {@inheritDoc}
*/
@Override
public Set<GenericName> getNames() throws DataStoreException {
return manager.getTypeNames();
}
/**
* {@inheritDoc}
*/
@Override
public void createFeatureType(FeatureType featureType) throws DataStoreException {
try {
manager.addSchema(featureType.getName(), featureType);
} catch (URISyntaxException e) {
throw new DataStoreException("We're unable to add a schema because we can't access source files.", e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void updateFeatureType(FeatureType featureType) throws DataStoreException {
throw new DataStoreException("Can not update MIF schema.");
}
/**
* {@inheritDoc}
*/
@Override
public void deleteFeatureType(String typeName) throws DataStoreException {
manager.deleteSchema(typeName);
removeFeatures(typeName, null);
}
/**
* {@inheritDoc}
*/
@Override
public FeatureType getFeatureType(String typeName) throws DataStoreException {
return manager.getType(typeName);
}
/**
* {@inheritDoc}
*/
@Override
public QueryCapabilities getQueryCapabilities() {
return queryCapabilities;
}
/**
* {@inheritDoc}
*/
@Override
public List<FeatureId> addFeatures(String groupName, Collection<? extends Feature> newFeatures, Hints hints) throws DataStoreException {
final List<FeatureId> addedFeatures;
try(final FeatureWriter writer = getFeatureWriter(QueryBuilder.all(groupName))) {
// We remove the features as we get them. We don't need to write them as the default writing behaviour is append mode.
while (writer.hasNext()) {
writer.next();
writer.remove();
}
if(manager.getWrittenCRS() != null) {
final FeatureCollection toWrite;
final FeatureType type = ((FeatureCollection)newFeatures).getFeatureType();
if(newFeatures instanceof FeatureCollection) {
toWrite = GenericDecoratedFeatureIterator.wrap( (FeatureCollection) newFeatures, new ReprojectFeatureType(type, manager.getWrittenCRS()));
} else {
toWrite = GenericDecoratedFeatureIterator.wrap(
FeatureStoreUtilities.collection(newFeatures.toArray(new Feature[newFeatures.size()])),
new ReprojectFeatureType(type, manager.getWrittenCRS()));
}
addedFeatures = FeatureStoreUtilities.write(writer, toWrite);
} else {
addedFeatures = FeatureStoreUtilities.write(writer, newFeatures);
}
}
return addedFeatures;
}
/**
* The update operation is not supported for now, because of the behaviour of the writer, which have to manage
* multiple feature types.
*/
@Override
public void updateFeatures(String groupName, Filter filter, Map<String, ? extends Object> values) throws DataStoreException {
//handleUpdateWithFeatureWriter(groupName, filter, values);
throw new UnsupportedOperationException("Update operation is not supported now");
}
/**
* The remove operation is not supported for now, because of the behaviour of the writer, which have to manage
* multiple feature types.
*/
@Override
public void removeFeatures(String groupName, Filter filter) throws DataStoreException {
//handleRemoveWithFeatureWriter(groupName, filter);
throw new UnsupportedOperationException("Remove operation is not supported now");
}
/**
* {@inheritDoc}
*/
@Override
public FeatureReader getFeatureReader(Query query) throws DataStoreException {
typeCheck(query.getTypeName());
return handleRemaining(new MIFFeatureReader(manager, query.getTypeName()), query);
}
/**
* {@inheritDoc}
*/
@Override
public FeatureWriter getFeatureWriter(Query query) throws DataStoreException {
typeCheck(query.getTypeName());
final MIFFeatureReader reader = new MIFFeatureReader(manager, query.getTypeName());
final MIFFeatureWriter writer = new MIFFeatureWriter(manager, reader);
return writer;
}
/**
* {@inheritDoc}
*/
@Override
public void refreshMetaModel() throws IllegalNameException {
manager.refreshMetaModel();
}
/**
* MIF file defines a delimiter character to separate values into the MID file. This function allows user to redefine it.
* @param newDelimiter The new delimiter to use for MID value separation.
*/
public void setDelimiter(char newDelimiter) {
manager.setDelimiter(newDelimiter);
}
public static boolean isCompatibleCRS(CoordinateReferenceSystem source) {
boolean isCompatible = false;
try {
final String mifCRS = ProjectionUtils.crsToMIFSyntax(source);
if (mifCRS != null && !mifCRS.isEmpty()) {
isCompatible = true;
}
} catch (Exception e) {
// Nothing to do here, if we get an exception, we just get an incompatible CRS.
}
return isCompatible;
}
}