ShapefileProcessor.java

package com.reallifedeveloper.tools.gis;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.geotools.api.data.DataStore;
import org.geotools.api.data.DataStoreFinder;
import org.geotools.api.data.SimpleFeatureSource;
import org.geotools.api.feature.Feature;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;

/**
 * Reads a <a href="http://en.wikipedia.org/wiki/Shapefile">shapefile</a> and processes it in some way.
 * <p>
 * One example of use is to create a version that converts a shapefile into SQL insert statements.
 *
 * @author RealLifeDeveloper
 */
public final class ShapefileProcessor {

    /**
     * The character encoding that is used by default, {@value #DEFAULT_CHARACTER_ENCODING}.
     */
    public static final String DEFAULT_CHARACTER_ENCODING = "UTF-8";

    private final FeatureProcessor featureProcessor;
    private String characterEncoding = DEFAULT_CHARACTER_ENCODING;

    /**
     * Creates a new {@code ShapefileProcessor} that processes features in a shapefile using the given {@link FeatureProcessor}.
     *
     * @param featureProcessor the {@code FeatureProcessor} to use, must not be {@code null}
     */
    public ShapefileProcessor(FeatureProcessor featureProcessor) {
        if (featureProcessor == null) {
            throw new IllegalArgumentException("featureProcessor must not be null");
        }
        this.featureProcessor = featureProcessor;
    }

    /**
     * Processes a shapefile read from the given URL with the given {@link FeatureProcessor} that determines what to do with each individual
     * {@code org.opengis.feature.Feature} in the file.
     *
     * @param shapefileUrl a URL to the shapefile
     *
     * @throws IOException if there was a problem reading the shapefile
     */
    public void processShapefile(URL shapefileUrl) throws IOException {
        if (shapefileUrl == null) {
            throw new IllegalArgumentException("shapefileUrl must not be null");
        }
        Map<String, Object> connectParams = new HashMap<>();
        connectParams.put("url", shapefileUrl);
        connectParams.put("charset", characterEncoding);

        DataStore dataStore = DataStoreFinder.getDataStore(connectParams);
        if (dataStore == null) {
            throw new FileNotFoundException(shapefileUrl.toString());
        }
        String[] typeNames = dataStore.getTypeNames();
        String typeName = typeNames[0];

        SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
        SimpleFeatureCollection collection = featureSource.getFeatures();

        try (SimpleFeatureIterator iterator = collection.features()) {
            while (iterator.hasNext()) {
                featureProcessor.processFeature(iterator.next());
            }
        }
    }

    /**
     * Sets the character encoding to use in the shapefile that is created.
     *
     * @param newCharacterEncoding the new character encoding
     */
    public void setCharacterEncoding(String newCharacterEncoding) {
        this.characterEncoding = newCharacterEncoding;
    }

    /**
     * Defines some kind of processing av an {@code org.opengis.feature.Feature}. A {@code Feature} represents a composite object in a
     * shapefile that contains both a geographical or geometrical object and also other attributes.
     */
    @FunctionalInterface
    public interface FeatureProcessor {
        /**
         * Processes the given {@code org.opengis.feature.Feature} in some way.
         *
         * @param feature the {@code Feature} to process
         */
        void processFeature(Feature feature);
    }
}