Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code for getting a custom area (background only) #1

Closed
michaelzangl opened this issue Jun 2, 2019 · 3 comments
Closed

Code for getting a custom area (background only) #1

michaelzangl opened this issue Jun 2, 2019 · 3 comments

Comments

@michaelzangl
Copy link
Collaborator

michaelzangl commented Jun 2, 2019

Not the nicest code, but it works

        MapView mv = MainApplication.getMap().mapView;
        // The area to take the image from.
        MapViewState.MapViewRectangle selectedArea = mv.getState().getViewArea(r);
        // The area in east/north coordinates
        ProjectionBounds selectedInEastNorth = selectedArea.getProjectionBounds();

        Optional<Layer> layerOpt = MainApplication.getLayerManager().getLayers().stream()
                .filter(l -> l.isBackgroundLayer() && l.isVisible())
                .findFirst();
        if (layerOpt.isPresent()) {
            Layer layer = layerOpt.get();

            MainLayerManager layersToRender = new MainLayerManager();
            layersToRender.addLayer(layer);
            MapView fakeMapView = new MapView(layersToRender, null) {
                {
                    setBounds(0, 0, 1024, 1024); // < max rendering size
                    updateLocationState();
                }

                @Override
                protected boolean isVisibleOnScreen() {
                    return true;
                }

                @Override
                public Point getLocationOnScreen() {
                    return new Point(0, 0);
                }
            };

            Runnable reset = () -> {};
            // Dirty hack. This should be fixed in JOSM.
            if (layer instanceof AbstractTileSourceLayer) {
                try {
                    Field coordinateConverterField = AbstractTileSourceLayer.class.getDeclaredField("coordinateConverter");
                    coordinateConverterField.setAccessible(true);
                    Field tileSourceField = TileCoordinateConverter.class.getDeclaredField("tileSource");
                    Field settingsField = TileCoordinateConverter.class.getDeclaredField("settings");
                    TileCoordinateConverter oldConverter = (TileCoordinateConverter) coordinateConverterField.get(layer);
                    tileSourceField.setAccessible(true);
                    settingsField.setAccessible(true);
                    TileCoordinateConverter newConverter = new TileCoordinateConverter(fakeMapView,
                            (TileSource) tileSourceField.get(oldConverter),
                            (TileSourceDisplaySettings) settingsField.get(oldConverter));
                    coordinateConverterField.set(layer, newConverter);
                    reset = () -> {
                        try {
                            coordinateConverterField.set(layer, oldConverter);
                        } catch (IllegalAccessException ex) {
                            Logging.warn(ex);
                        }
                    };
                } catch (NoSuchFieldException | IllegalAccessException ex) {
                    // TODO: Show error
                    Logging.error(ex);
                }
            }

            MapViewPaintable.LayerPainter painter = layer.attachToMapView(new MapViewPaintable.MapViewEvent(fakeMapView, false));
            // Mind that this will not exactly zoom to that area, but instead to an area close to it depending on the native scale of the background layer.
            fakeMapView.zoomTo(selectedArea.getCornerBounds());
            // Now we need to find out the selected are in faked map view space
            MapViewState.MapViewRectangle toPaint = fakeMapView.getState().getPointFor(selectedInEastNorth.getMin())
                    .rectTo(fakeMapView.getState().getPointFor(selectedInEastNorth.getMax()));

            // Actual drawing
            BufferedImage image = new BufferedImage((int) toPaint.getInView().getWidth(), (int) toPaint.getInView().getHeight(),
                    BufferedImage.TYPE_BYTE_INDEXED);

            Graphics2D graphics = image.createGraphics();
            // Move so that the image matches the region we are painting.
            graphics.translate(-toPaint.getInView().getMinX(), -toPaint.getInView().getMinY());
            painter.paint(new MapViewGraphics(fakeMapView, graphics, toPaint));

            // Dispose the painter, release resources.
            graphics.dispose();
            painter.detachFromMapView(new MapViewPaintable.MapViewEvent(fakeMapView, false));
            /* TODO: We could dispose layers. But this will make some global state of JOSM inconsistent.
             * So we just let GC reclaim as many resources as possible. */
            reset.run();

            // Good for debugging / scaling: How many meters are in 100 pixel for your image
            // I don't know if your training model will improve if you normalize the images by using this, any way you should store it just in case we need it later.
            fakeMapView.getDist100Pixel();

            try {
                ImageIO.write(image, "png", new File("whatever.png"));
            } catch (IOException ex) {
                //TODO: Show error
            }
        } else {
            JOptionPane.showMessageDialog(
                    MainApplication.getMainFrame(),
                    tr("No imagery layer found."),
                    tr("Warning"),
                    JOptionPane.WARNING_MESSAGE);
        }

Mind the copyright issues: Before publishing (parts of) satellite images online, you should check if that source allows them to be published. Bing does not allow it.

@BBloggsbott
Copy link
Owner

Okay. Thank you for the code.
I'll keep the copyright issues in my mind 🙂

@BBloggsbott
Copy link
Owner

Every time the above code snippet is run, the imagery in the map view pixellates and becomes normal. I moved the downloader to the separate thread so that the user will be able to perform other operations when this download is happening.

@BBloggsbott
Copy link
Owner

Sometimes downloads low res image even if high res image is available and can be downloaded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants