Shapefile – How to Export Shapefile to SVG Using Java and GeoTools

geotoolsjavashapefilesvg

I am currently working on how to read in ESRI shapefiles, parse/edit layers and then export out to SVG (Scalable Vector Graphic) file type. I have been using GeoTools primarily for reading/loading shapefiles. I am also displaying them using an JMapFrame GUI.

My issue currently is how to export a shapefile, after reading it in, to an SVG file. There is a great example by OpenJUMP which does exactly as I need, however, while looking at the code used to implement this feature, I am unable to figure out how to do it myself. I want to avoid using OpenJUMP's program as export to SVG using java code (so I can do other things before exporting).

Note: I have 2 files – ShapefileExporter.java (main class) and ExportShapefile.java (export to svg code)

Here is some of my code reading in shapefiles:

    public class ShapefileExporter {    
        final static ExportShapefile exportSHP = new ExportShapefile();
        final JFileChooser jfc = new JFileChooser();
        JToolBar jtb;
        MapContent map;
        JPopupMenu rcMenu = new JPopupMenu("Popup");
        private JMapFrame jframe;
        private File shapeFile;

        // Main, start program
        public static void main(String[] args) throws Exception {

            ShapefileExporter exporter = new ShapefileExporter();

            if (args.length == 0 || !args[0].toLowerCase().endsWith(".shp")) {
                System.out.println("Any provided shapefile.prl or .sld will be used");
            } else {
                String pathname = args[0];
                URL shape = acquireURL(pathname);

                if (shape == null) {
                    System.err.println("Could not find shapefile");
                    System.exit(1);
                }

                exporter.loadFile(shape);
            }
        }

        // Load Shapefile into JMapFrame
        protected void loadFile(URL shape) throws IOException {

            FileDataStore store = FileDataStoreFinder.getDataStore(shape);
            SimpleFeatureSource featureSource = store.getFeatureSource();

            Style style = SLD.createSimpleStyle( featureSource.getSchema() );
            FeatureLayer layer = new FeatureLayer(featureSource, style);  

            ShapefileDataStore ds = new ShapefileDataStore(shape);
            FeatureSource<SimpleFeatureType, SimpleFeature> fs = ds.getFeatureSource();

            map = new MapContent();
            map.addLayer(layer);


            jframe.setMapContent(map);
        }

        // Constructor, create/display JMapFrame GUI
        public ShapefileExporter() {

            File default_dir = new File(System.getProperty("user.home") + System.getProperty("file.separator") + "NetBeansProjects/Working On");
            jfc.setCurrentDirectory(default_dir);

            jframe = new JMapFrame();

            jframe.setSize(1000, 800);
            jframe.enableStatusBar(true);
            jframe.enableToolBar(true);
            jframe.enableLayerTable(true);

            JMenuBar menuBar = new JMenuBar();
            jframe.setJMenuBar(menuBar);

            JMenu menubar = new JMenu("File");
            menuBar.add(menubar);

            menubar.add(new SafeAction("Load file") {
                @Override
                public void action(ActionEvent e) throws Throwable {


                    setJFCFilter("Shapefiles", "shp");
                    int returnVal = jfc.showOpenDialog(jframe);

                    if (returnVal == JFileChooser.APPROVE_OPTION) {
                        String pathname = jfc.getSelectedFile().getAbsolutePath();
                        URL shape = acquireURL(pathname);

                        if (shape == null) {
                            JOptionPane.showMessageDialog(jframe, "could not find file \"" + pathname + "\"", "Could not find file",
                                    JOptionPane.ERROR_MESSAGE);
                        }

                        try {
                            shapeFile = new File(pathname);
                            loadFile(shape);
                        } catch (IOException ex) {
                            System.err.println("File exception for file: " + shape);
                        }
                    }
                }
            });

            menubar.add(new SafeAction("Export Shapefile") {
                @Override
                public void action(ActionEvent e) throws Throwable {

                    File svgFile = changeFileExt(shapeFile, ".svg");
                    jfc.setSelectedFile(svgFile);

                    setJFCFilter("Scaleable Vector Graphics", "svg");
                    int returnVal = jfc.showSaveDialog(jframe);

                    if (returnVal == JFileChooser.APPROVE_OPTION) {
                        File selFile = jfc.getSelectedFile();
                        //File svgfile = changeFileExt(shpfile, ".svg");
                        //URL svg = acquireURL(shpfile);
                        Path path = Paths.get(selFile.getPath());



                        if (Files.notExists(path, LinkOption.NOFOLLOW_LINKS)) {
                            FileOutputStream out = new FileOutputStream(new File(path.toString()));
                            out.close();
                        }

                        if (selFile == null) {
                            JOptionPane.showMessageDialog(jframe, "could not find file \"" + selFile + "\"", "Could not find file",
                                    JOptionPane.ERROR_MESSAGE);
                        }

                        try {

                            System.out.println("SvG File: " + selFile);
                            System.out.println();

                            if (Files.exists(path, LinkOption.NOFOLLOW_LINKS)) {
                                exportSHP.exportToSVG(new File(selFile.getPath()), jframe, map);
                            }
                        } catch (Exception ex) {
                            System.err.println("File exception for file: " + selFile + "\n" + ex);
                        }
                    }
                }
            });


            jframe.setVisible(true);

        } // End constructor
} // End class

Export to SVG Code

public class ExportShapefile {
    public void exportToSVG(File file, JMapFrame jframe, MapContent map)
                throws ParserConfigurationException {

            Dimension canvasSize = new Dimension(jframe.getMapPane().getSize());

            Document doc;

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db;

            db = dbf.newDocumentBuilder();

            // Create an instance of org.w3c.dom.Document
            doc = db.getDOMImplementation().createDocument(null, "svg", null);

            // Set up the map
            SVGGeneratorContext ctx1 = SVGGeneratorContext.createDefault(doc);
            SVGGeneratorContext ctx = ctx1;
            ctx.setComment("Generated by GeoTools with Batik SVG Generator");

            SVGGraphics2D g2d = new SVGGraphics2D(ctx, true);

            g2d.setSVGCanvasSize(canvasSize);

            Rectangle outputArea = new Rectangle(g2d.getSVGCanvasSize());
            ReferencedEnvelope dataArea = map.getViewport().getBounds();

            jframe.getMapPane().getRenderer().paint(g2d, outputArea, dataArea);
            OutputStreamWriter osw = null;

            try {
                OutputStream out = new FileOutputStream(file);
                osw = null;

                osw = new OutputStreamWriter(out, "UTF-8");
                g2d.stream(osw);

            } catch (FileNotFoundException | UnsupportedEncodingException |
                    SVGGraphics2DIOException ex) {
                System.err.println("IOException found: " + ex);
            } finally {
                if (osw != null) {
                    try {
                        osw.close();
                    } catch (IOException ex) {
                        System.err.println("IOException found: " + ex);
                    }
                }
            }
        }
}

pom.xml file (maven version)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- =========================================================== -->
<!--     Project Description                                     -->
<!-- =========================================================== -->
<groupId>com.mycompany</groupId>
<artifactId>ShpExportMaven</artifactId>
<version>14-SNAPSHOT</version>
<packaging>jar</packaging>

<name>ShpExportMaven</name>
<url>http://maven.apache.org</url>

<properties>
    <oracle>false</oracle>
    <allow.test.skip>true</allow.test.skip>
    <allow.test.failure.ignore>false</allow.test.failure.ignore>
    <extensive.tests>false</extensive.tests>
    <skip.image.tests>false</skip.image.tests>
    <interactive.tests>false</interactive.tests>
    <interactive.image>false</interactive.image>
    <online.skip.pattern>**/*OnlineTest.java</online.skip.pattern>
    <stress.skip.pattern>**/*StressTest.java</stress.skip.pattern>
    <test.exclude.pattern>disabled</test.exclude.pattern>
    <test.maxHeapSize>256M</test.maxHeapSize>
    <javadoc.maxHeapSize>1536M</javadoc.maxHeapSize>
    <test.forkMode>once</test.forkMode>
    <src.output>${basedir}/target</src.output>
    <imageio.ext.version>1.1.12</imageio.ext.version>
    <jaiext.version>1.0.3</jaiext.version>
    <netcdf.version>4.5.5</netcdf.version>
    <jt.version>1.3.1</jt.version>
    <jvm.opts></jvm.opts>
    <maven.build.timestamp.format>dd-MMM-yyyy HH:mm</maven.build.timestamp.format>
    <build.timestamp>${maven.build.timestamp}</build.timestamp>
    <fork.javac>true</fork.javac>
    <javac.maxHeapSize>256M</javac.maxHeapSize>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <postgresql.jdbc.version>9.4-1201-jdbc41</postgresql.jdbc.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.java.dev.jsr-275</groupId>
            <artifactId>jsr-275</artifactId>
            <version>1.0-beta-2</version>
        </dependency>

        <!-- Mathematic, geometries, SVG... -->
        <dependency>
            <groupId>com.vividsolutions</groupId>
            <artifactId>jts</artifactId>
            <version>1.13</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svggen</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-awt-util</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-dom</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-util</artifactId>
            <version>1.6.1</version>
        </dependency>

        <!-- Java Advabnced Imaging (JAI) -->
        <dependency>
            <groupId>javax.media</groupId>
            <artifactId>jai_core</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>javax.media</groupId>
            <artifactId>jai_imageio</artifactId>
            <version>1.1</version>
        </dependency>

        <!-- ImageIO-Ext -->
        <dependency>
            <groupId>it.geosolutions.imageio-ext</groupId>
            <artifactId>imageio-ext-tiff</artifactId>
            <version>${imageio.ext.version}</version>
        </dependency>
        <dependency>
            <groupId>it.geosolutions.imageio-ext</groupId>
            <artifactId>imageio-ext-utilities</artifactId>
            <version>${imageio.ext.version}</version>
        </dependency>

        <!-- XML -->
        <dependency>
            <groupId>org.jdom</groupId>
            <artifactId>jdom</artifactId>
            <version>1.1.3</version>
        </dependency>

        <!-- Apache -->
        <dependency>
            <groupId>commons-pool</groupId>
            <artifactId>commons-pool</artifactId>
            <version>1.5.4</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.2</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>

        <!-- Databases -->
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>2.3.0</version>
        </dependency>

    </dependencies>
</dependencyManagement>

<!-- Inherited by all modules -->
<dependencies>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-api</artifactId>
        <version>14.0</version>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-main</artifactId>
        <version>14.0</version>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-shapefile</artifactId>
        <version>14.0</version>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-render</artifactId>
        <version>14.0</version>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-swing</artifactId>
        <version>14.0</version>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>batik</groupId>
        <artifactId>batik-awt-util</artifactId>
        <version>1.6-1</version>
    </dependency>
    <dependency>
        <groupId>batik</groupId>
        <artifactId>batik-dom</artifactId>
        <version>1.6-1</version>
    </dependency>
    <dependency>
        <groupId>batik</groupId>
        <artifactId>batik-svggen</artifactId>
        <version>1.6-1</version>
    </dependency>
    <dependency>
        <groupId>batik</groupId>
        <artifactId>batik</artifactId>
        <version>1.5</version>
    </dependency>
</dependencies>

<!-- =========================================================== -->
<!--     Build Configuration                                     -->
<!-- =========================================================== -->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.mycompany.shapefileexporter.mvn.ShapefileExporter</mainClass>
                    </manifest>
                </archive>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>
    </plugins>
</build>

<!-- ================================================================== -->
<!--     Repositories. This is where Maven looks for dependencies. The  -->
<!--     Maven repository is implicit and doesn't need to be specified. -->
<!-- ================================================================== -->
<repositories>
    <repository>
        <id>central.maven.org</id>
        <name>Central Maven repository</name>
        <url>http://central.maven.org/maven2</url>
    </repository>
    <repository>
        <id>java.net-Public</id>
        <name>Maven Java Net Snapshots and Releases</name>
        <url>https://maven.java.net/content/groups/public/</url>
    </repository>


    <repository>
        <id>geosolutions</id>
        <name>geosolutions repository</name>
        <url>http://maven.geo-solutions.it/</url>
    </repository>

    <repository>
        <id>osgeo</id>
        <name>Open Source Geospatial Foundation Repository</name>
        <url>http://download.osgeo.org/webdav/geotools/</url>
    </repository>

    <repository>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        <id>boundless</id>
        <name>Boundless Maven Repository</name>
        <url>http://repo.boundlessgeo.com/main</url>
    </repository>

</repositories>

<!-- =========================================================== -->
<!--     Plugin repositories.                                    -->
<!--     This is where Maven looks for plugin dependencies.      -->
<!-- =========================================================== -->
<pluginRepositories>
    <pluginRepository>
        <id>osgeo</id>
        <name>Open Source Geospatial Foundation Maven 2 Repository</name>
        <url>http://download.osgeo.org/webdav/geotools/</url>
        <releases>
            <enabled>true</enabled>
        </releases>
    </pluginRepository> 
    <pluginRepository>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        <id>boundless</id>
        <name>Boundless Maven Repository</name>
        <url>http://repo.boundlessgeo.com/main</url>
    </pluginRepository> 
    <pluginRepository>
        <id>sonatype-snapshots</id>
        <name>sonatype-snapshots</name>
        <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </pluginRepository>
</pluginRepositories>

Note: 'file' 'jframe' and 'map' are being declared and pointed to from another file.

Example of JMapFrame display:

GIS Display

Update: I got the code working, there were some issues with the pom.xml and versions of libraries I had.

Best Answer

Have a look at the code referenced in my blog post which shows how to do exactly this, including styling the layer.

Here is the actual code:

private void exportSVG(File file) {

Dimension canvasSize = new Dimension(frame.getMapPane().getSize());

Document document = null;

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
try {
    db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
    return;
}

// Create an instance of org.w3c.dom.Document
document = db.getDOMImplementation().createDocument(null, "svg", null);

// Set up the map
SVGGeneratorContext ctx1 = SVGGeneratorContext.createDefault(document);
SVGGeneratorContext ctx = ctx1;
ctx.setComment("Generated by GeoTools with Batik SVG Generator");

SVGGraphics2D g2d = new SVGGraphics2D(ctx, true);

g2d.setSVGCanvasSize(canvasSize);

Rectangle outputArea = new Rectangle(g2d.getSVGCanvasSize());
ReferencedEnvelope dataArea = map.getViewport().getBounds();

frame.getMapPane().getRenderer().paint(g2d, outputArea, dataArea);
OutputStreamWriter osw = null;
try {
    OutputStream out = new FileOutputStream(file);
    osw = null;

    osw = new OutputStreamWriter(out, "UTF-8");
    g2d.stream(osw);
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (UnsupportedEncodingException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (SVGGraphics2DIOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} finally {
    if (osw != null)
    try {
        osw.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
}
Related Question