/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.importer.format;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FilenameUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.CatalogBuilder;
import org.geoserver.catalog.CatalogFactory;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.AttributeTypeInfo;
import org.geotools.data.FeatureReader;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.util.logging.Logging;
import org.geoserver.importer.Directory;
import org.geoserver.importer.FileData;
import org.geoserver.importer.ImportData;
import org.geoserver.importer.ImportTask;
import org.geoserver.importer.VectorFormat;
import org.geoserver.importer.job.ProgressMonitor;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
public class GeoJSONFormat extends VectorFormat {
static Logger LOG = Logging.getLogger(GeoJSONFormat.class);
private static ReferencedEnvelope EMPTY_BOUNDS = new ReferencedEnvelope();
@Override
public FeatureReader read(ImportData data, ImportTask item) throws IOException {
final SimpleFeatureType featureType =
(SimpleFeatureType) item.getMetadata().get(FeatureType.class);
FeatureJSON json = new FeatureJSON();
json.setFeatureType(featureType);
final FeatureIterator it = json.streamFeatureCollection(file(data, item));
return new FeatureReader() {
@Override
public FeatureType getFeatureType() {
return featureType;
}
@Override
public boolean hasNext() throws IOException {
return it.hasNext();
}
@Override
public Feature next() throws IOException, IllegalArgumentException,
NoSuchElementException {
return it.next();
}
@Override
public void close() throws IOException {
it.close();
}
};
}
@Override
public void dispose(FeatureReader reader, ImportTask item) throws IOException {
reader.close();
}
@Override
public int getFeatureCount(ImportData data, ImportTask item) throws IOException {
return -1;
}
@Override
public String getName() {
return "GeoJSON";
}
@Override
public boolean canRead(ImportData data) throws IOException {
Optional<File> file = maybeFile(data);
if (file.isPresent()) {
return sniff(file.get()) != null;
}
return false;
}
SimpleFeature sniff(File file) {
try {
FeatureIterator it = new FeatureJSON().streamFeatureCollection(file);
try {
if (it.hasNext()) {
return (SimpleFeature) it.next();
}
}
finally {
it.close();
}
} catch (Exception e) {
LOG.log(Level.FINER, "Error reading file as json", e);
}
return null;
}
@Override
public StoreInfo createStore(ImportData data, WorkspaceInfo workspace, Catalog catalog)
throws IOException {
// direct import not supported
return null;
}
@Override
public List<ImportTask> list(ImportData data, Catalog catalog, ProgressMonitor monitor)
throws IOException {
if (data instanceof Directory) {
List<ImportTask> tasks = new ArrayList<ImportTask>();
for (FileData file : ((Directory)data).getFiles()) {
tasks.add(task(file, catalog));
}
return tasks;
}
else {
return Arrays.asList(task(data, catalog));
}
}
ImportTask task(ImportData data, Catalog catalog) throws IOException {
File file = maybeFile(data).get();
CatalogFactory factory = catalog.getFactory();
CatalogBuilder catalogBuilder = new CatalogBuilder(catalog);
// get the composite feature type
SimpleFeatureType featureType = new FeatureJSON().readFeatureCollectionSchema(file, false);
LOG.log(Level.FINE, featureType.toString());
SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
tb.init(featureType);
tb.setName(FilenameUtils.getBaseName(file.getName()));
featureType = tb.buildFeatureType();
// create the feature type
FeatureTypeInfo ft = catalog.getFactory().createFeatureType();
ft.setName(FilenameUtils.getBaseName(file.getName()));
ft.setNativeName(ft.getName());
List<AttributeTypeInfo> attributes = ft.getAttributes();
for (AttributeDescriptor ad : featureType.getAttributeDescriptors()) {
AttributeTypeInfo att = factory.createAttribute();
att.setName(ad.getLocalName());
att.setBinding(ad.getType().getBinding());
attributes.add(att);
}
// crs
CoordinateReferenceSystem crs = null;
if (featureType != null && featureType.getCoordinateReferenceSystem() != null) {
crs = featureType.getCoordinateReferenceSystem();
}
try {
crs = crs != null ? crs : CRS.decode("EPSG:4326");
}
catch (Exception e) {
throw new IOException(e);
}
ft.setNativeCRS(crs);
String srs = srs(crs);
if (srs != null) {
ft.setSRS(srs);
}
// bounds
ft.setNativeBoundingBox(EMPTY_BOUNDS);
ft.setLatLonBoundingBox(EMPTY_BOUNDS);
ft.getMetadata().put("recalculate-bounds", Boolean.TRUE);
LayerInfo layer = catalogBuilder.buildLayer((ResourceInfo) ft);
ImportTask task = new ImportTask(data);
task.setLayer(layer);
task.getMetadata().put(FeatureType.class, featureType);
return task;
}
File file(ImportData data, final ImportTask item) {
if (data instanceof Directory) {
return Iterables.find(((Directory) data).getFiles(), new Predicate<FileData>() {
@Override
public boolean apply(FileData input) {
return FilenameUtils.getBaseName(input.getFile().getName())
.equals(item.getLayer().getName());
}
}).getFile();
}
else {
return maybeFile(data).get();
}
}
Optional<File> maybeFile(ImportData data) {
if (data instanceof FileData) {
return Optional.of(((FileData) data).getFile());
}
return Optional.absent();
}
String srs(CoordinateReferenceSystem crs) {
Integer epsg = null;
try {
epsg = CRS.lookupEpsgCode(crs, false);
if (epsg == null) {
epsg = CRS.lookupEpsgCode(crs, true);
}
}
catch(Exception e) {
LOG.log(Level.FINER, "Error looking up epsg code", e);
}
return epsg != null ? "EPSG:" + epsg : null;
}
}