0

I have very strange problem with rendering the world map as image with geotools when I run my jar file. At the same time, when I compiling my project inside of the NetBeans IDE everything is Ok. What might be wrong?

Here is full stack trace of the Exception I've got

D:\test>java -jar myrunnablejar-1.0-SNAPSHOT.jar
Sun Microsystems, Inc.
1.1
Java Advanced Imaging Image I/O Tools

INFO: Begin of the image rendering
imageBounds
X: 0.0 Y: 0.0 width: 3000.0 height: 1447.0
mapBounds
Max X: 180.0 Min X: -180.0 Max Y: 83.62359619140625 Min Y: -90.0
ш■ы 22, 2019 12:42:42 PM org.geotools.renderer.lite.StreamingRenderer fireErrorE
vent
SEVERE: Invalid measure '1.0', was expecting a number, eventually followed by px
, m or ft
java.lang.IllegalArgumentException: Invalid measure '1.0', was expecting a numbe
r, eventually followed by px, m or ft
        at org.geotools.styling.visitor.Measure.processLiteralExpression(Measure
.java:118)
        at org.geotools.styling.visitor.Measure.processLiteralExpression(Measure
.java:99)
        at org.geotools.styling.visitor.Measure.<init>(Measure.java:62)
        at org.geotools.styling.visitor.UomRescaleStyleVisitor.rescale(UomRescal
eStyleVisitor.java:92)
        at org.geotools.styling.visitor.UomRescaleStyleVisitor.rescaleStroke(Uom
RescaleStyleVisitor.java:141)
        at org.geotools.styling.visitor.UomRescaleStyleVisitor.visit(UomRescaleS
tyleVisitor.java:201)
        at org.geotools.styling.PolygonSymbolizerImpl.accept(PolygonSymbolizerIm
pl.java:130)
        at org.geotools.styling.visitor.DuplicatingStyleVisitor.copy(Duplicating
StyleVisitor.java:508)
        at org.geotools.styling.visitor.DuplicatingStyleVisitor.visit(Duplicatin
gStyleVisitor.java:271)
        at org.geotools.renderer.lite.StreamingRenderer.rescaleFeatureTypeStyle(
StreamingRenderer.java:2555)
        at org.geotools.renderer.lite.StreamingRenderer.applyUnitRescale(Streami
ngRenderer.java:2408)
        at org.geotools.renderer.lite.StreamingRenderer.createLiteFeatureTypeSty
les(StreamingRenderer.java:1966)
        at org.geotools.renderer.lite.StreamingRenderer.processStylers(Streaming
Renderer.java:2141)
        at org.geotools.renderer.lite.StreamingRenderer.paint(StreamingRenderer.
java:892)
        at org.geotools.renderer.lite.StreamingRenderer.paint(StreamingRenderer.
java:625)
        at wind.maps.crs.WorldMap.saveImage(WorldMap.java:167)
        at wind.maps.crs.WorldMap.display(WorldMap.java:214)
        at wind.maps.main.Main.main(Main.java:109)

ш■ы 22, 2019 12:42:47 PM wind.maps.crs.WorldMap saveImage
INFO: End of the image rendering
ш■ы 22, 2019 12:42:55 PM org.geotools.renderer.lite.StreamingRenderer fireErrorE
vent
SEVERE: Invalid measure '1.0', was expecting a number, eventually followed by px
, m or ft
java.lang.IllegalArgumentException: Invalid measure '1.0', was expecting a numbe
r, eventually followed by px, m or ft
        at org.geotools.styling.visitor.Measure.processLiteralExpression(Measure
.java:118)
        at org.geotools.styling.visitor.Measure.processLiteralExpression(Measure
.java:99)
        at org.geotools.styling.visitor.Measure.<init>(Measure.java:62)
        at org.geotools.styling.visitor.UomRescaleStyleVisitor.rescale(UomRescal
eStyleVisitor.java:92)
        at org.geotools.styling.visitor.UomRescaleStyleVisitor.rescaleStroke(Uom
RescaleStyleVisitor.java:141)
        at org.geotools.styling.visitor.UomRescaleStyleVisitor.visit(UomRescaleS
tyleVisitor.java:201)
        at org.geotools.styling.PolygonSymbolizerImpl.accept(PolygonSymbolizerIm
pl.java:130)
        at org.geotools.styling.visitor.DuplicatingStyleVisitor.copy(Duplicating
StyleVisitor.java:508)
        at org.geotools.styling.visitor.DuplicatingStyleVisitor.visit(Duplicatin
gStyleVisitor.java:271)
        at org.geotools.renderer.lite.StreamingRenderer.rescaleFeatureTypeStyle(
StreamingRenderer.java:2555)
        at org.geotools.renderer.lite.StreamingRenderer.applyUnitRescale(Streami
ngRenderer.java:2408)
        at org.geotools.renderer.lite.StreamingRenderer.createLiteFeatureTypeSty
les(StreamingRenderer.java:1966)
        at org.geotools.renderer.lite.StreamingRenderer.processStylers(Streaming
Renderer.java:2141)
        at org.geotools.renderer.lite.StreamingRenderer.paint(StreamingRenderer.
java:892)
        at org.geotools.swing.RenderingTask.call(RenderingTask.java:101)
        at org.geotools.swing.RenderingTask.call(RenderingTask.java:38)
        at java.util.concurrent.FutureTask.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

Here is my code:

public class WorldMap {

private static final Logger LOGGER = Logger.getLogger(WorldMap.class.getName()); 
final static String INPUT = "./shape/world.shp";
final static String OUTPUT = "./MAPS/";

private File shapeFile;
private FileDataStoreFactorySpi factory;
private FileDataStore fileDataStore;
private CoordinateReferenceSystem crs;
private SimpleFeatureSource featureSource;
private final Style style;
private final MapContent map;
private final GeometryFactory geometryFactory; 
private final MathTransformFactory mtFactory;
private final CRSFactory crsFactory;

public WorldMap() throws IOException {
    this.factory = new ShapefileDataStoreFactory();
    this.shapeFile = new File(INPUT);        
    this.fileDataStore = (FileDataStore) getDataStore();
    this.featureSource = fileDataStore.getFeatureSource();
    this.style = SLD.createSimpleStyle(featureSource.getSchema());
    this.map = new MapContent();
    this.geometryFactory = JTSFactoryFinder.getGeometryFactory( null );
    this.mtFactory = ReferencingFactoryFinder.getMathTransformFactory(null);
    this.crsFactory = ReferencingFactoryFinder.getCRSFactory(null);        
}

private DataStore getDataStore(){
    DataStore dataStore = null;
    try {
        Map<String, Serializable> params = new HashMap<String, Serializable>();
        params.put(ShapefileDataStoreFactory.URLP.key, URLs.fileToUrl(shapeFile));
        dataStore = factory.createDataStore(params);

    } catch (IOException ex) {
        Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
    }
    return dataStore;
}

public void saveImage(final MapContent map, final String file, int imageWidth) {

    try {
        LOGGER.log(Level.INFO, "Begin of the image rendering");

        GTRenderer renderer = new StreamingRenderer();
        renderer.setMapContent(map);

        Rectangle imageBounds = null;
        ReferencedEnvelope mapBounds = null;

        mapBounds = map.getViewport().getBounds();
        double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
        imageBounds = new Rectangle(0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

        BufferedImage image = new BufferedImage((int)imageBounds.getWidth(), (int)imageBounds.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics2D gr = image.createGraphics();

        gr.setPaint(Color.WHITE);
        gr.fill(imageBounds);

        System.out.println("imageBounds");
        System.out.println("X: " + imageBounds.getBounds().getX() + " Y: " + imageBounds.getBounds().getY() + " width: " + imageBounds.getWidth() + " height: " + imageBounds.getHeight());

        System.out.println("mapBounds");
        System.out.println("Max X: " + mapBounds.getMaxX() + " Min X: " + mapBounds.getMinX() + " Max Y: " + mapBounds.getMaxY() + " Min Y: " + mapBounds.getMinY());

        renderer.paint(gr, imageBounds, mapBounds);
        File fileToSave = new File(file);
        ImageIO.write(image, "png", fileToSave);

        LOGGER.log(Level.INFO, "End of the image rendering");
    } catch (IOException ex) {
        Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
    }

}

public void display(){        
    map.addLayer(new FeatureLayer(featureSource, style));  
    saveImage(map, OUTPUT + "image.png", 800);
    addJMapFrame();
}  

public void addJMapFrame(){
    JMapFrame mapFrame = new JMapFrame(map);
    mapFrame.enableToolBar(true);
    mapFrame.enableStatusBar(true);

    JToolBar toolbar = mapFrame.getToolBar();
    toolbar.addSeparator();

    mapFrame.setSize(2156, 1728);
    mapFrame.setVisible(true);        
}

class ValidateGeometryAction extends SafeAction {
    ValidateGeometryAction() {
        super("Validate geometry");
        putValue(Action.SHORT_DESCRIPTION, "Check each geometry");
    }

    @Override
    public void action(java.awt.event.ActionEvent ae) throws Throwable {
        int numInvalid = validateFeatureGeometry(null);
        String msg;
        if (numInvalid == 0) {
            msg = "All feature geometries are valid";
        } else {
            msg = "Invalid geometries: " + numInvalid;
        }
        JOptionPane.showMessageDialog(null, msg, "Geometry results", JOptionPane.INFORMATION_MESSAGE);
    }
}

private int validateFeatureGeometry(ProgressListener progress) throws Exception {
    final SimpleFeatureCollection featureCollection = featureSource.getFeatures();
    class ValidationVisitor implements FeatureVisitor {
        public int numInvalidGeometries = 0;
        @Override
        public void visit(Feature f) {
            SimpleFeature feature = (SimpleFeature) f;
            Geometry geom = (Geometry) feature.getDefaultGeometry();
            if (geom != null && !geom.isValid()) {
                numInvalidGeometries++;
                System.out.println("Invalid Geoemtry: " + feature.getID());
            }
        }
    }
    ValidationVisitor visitor = new ValidationVisitor();
    featureCollection.accepts(visitor, progress);
    return visitor.numInvalidGeometries;
}

class ExportShapefileAction extends SafeAction {
    ExportShapefileAction() {
        super("Export...");
        putValue(Action.SHORT_DESCRIPTION, "Export using current crs");
    }

    @Override
    public void action(java.awt.event.ActionEvent ae) throws Throwable {
        exportToShapefile();
    }
}

private void exportToShapefile() throws Exception {
    SimpleFeatureType schema = featureSource.getSchema();
    JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
    chooser.setDialogTitle("Save reprojected shapefile");
    chooser.setSaveFile(shapeFile);
    int returnVal = chooser.showSaveDialog(null);
    if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {
        return;
    }
    File file = chooser.getSelectedFile();
    if (file.equals(shapeFile)) {
        JOptionPane.showMessageDialog(null, "Cannot replace " + file);
        return;
    }

    CoordinateReferenceSystem dataCRS = schema.getCoordinateReferenceSystem();
    CoordinateReferenceSystem worldCRS = map.getCoordinateReferenceSystem();
    boolean lenient = true; 
    MathTransform transform = CRS.findMathTransform(dataCRS, worldCRS, lenient);

    SimpleFeatureCollection featureCollection = featureSource.getFeatures();

    DataStoreFactorySpi factory = new ShapefileDataStoreFactory();
    Map<String, Serializable> create = new HashMap<>();
    create.put("url", file.toURI().toURL());
    create.put("create spatial index", Boolean.TRUE);
    DataStore dataStore = factory.createNewDataStore(create);
    SimpleFeatureType featureType = SimpleFeatureTypeBuilder.retype(schema, worldCRS);
    dataStore.createSchema(featureType);

    String createdName = dataStore.getTypeNames()[0];

    Transaction transaction = new DefaultTransaction("Reproject");
    try (FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
                    dataStore.getFeatureWriterAppend(createdName, transaction);
            SimpleFeatureIterator iterator = featureCollection.features()) {
        while (iterator.hasNext()) {
            SimpleFeature feature = iterator.next();
            SimpleFeature copy = writer.next();
            copy.setAttributes(feature.getAttributes());

            Geometry geometry = (Geometry) feature.getDefaultGeometry();
            Geometry geometry2 = JTS.transform(geometry, transform);

            copy.setDefaultGeometry(geometry2);
            writer.write();
        }
        transaction.commit();
        JOptionPane.showMessageDialog(null, "Export to shapefile complete");
    } catch (Exception problem) {
        transaction.rollback();
        JOptionPane.showMessageDialog(null, "Export to shapefile failed");
    } finally {
        transaction.close();
    }
}


class ValidateGeometryAction2 extends SafeAction {
    ValidateGeometryAction2() {
        super("Validate geometry");
        putValue(Action.SHORT_DESCRIPTION, "Check each geometry");
    }

    @Override
    public void action(java.awt.event.ActionEvent ae) throws Throwable {
        SwingWorker worker =
                new SwingWorker<String, Object>() {
                    @Override
                    protected String doInBackground() throws Exception {
                        final JProgressWindow progress = new JProgressWindow(null);
                        progress.setTitle("Validating feature geometry");

                        int numInvalid = validateFeatureGeometry(progress);
                        if (numInvalid == 0) {
                            return "All feature geometries are valid";
                        } else {
                            return "Invalid geometries: " + numInvalid;
                        }
                    }

                    @Override
                    protected void done() {
                        try {
                            Object result = get();
                            JOptionPane.showMessageDialog(
                                    null,
                                    result,
                                    "Geometry results",
                                    JOptionPane.INFORMATION_MESSAGE);
                        } catch (HeadlessException | InterruptedException | ExecutionException ignore) {
                        }
                    }
                };
        worker.execute();
    }
}    

}

0

If your program works in your IDE but not when compiled into a jar, then you are assembling the jar badly. You first need to make sure all the GeoTools modules you are using (gt-main, gt-referencing, gt-shape, gt-rendering etc) are included in the jar.

Also, you have to make sure you handle the META-INF/services directory contents are correctly combined rather than just the last jar added overwriting the contents of the previous ones.

See the GeoTools FAQ to see a full example of using the Maven Shade plugin to handle this.

  • Which exactly files do I need to copy into the jar META-INF/services dir? I use world .shp, .sld attributes file, and .csv data files, which are created dynamically so I can't put them inside of the .jar. Do you mean that I should copy .shp file into the META-INF? – rishab Jul 22 at 14:46
  • No, see my updated answer – Ian Turton Jul 22 at 14:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.