All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.ird.observe.client.util.tripMap.ObserveMapPane Maven / Gradle / Ivy

package fr.ird.observe.client.util.tripMap;

/*
 * #%L
 * ObServe Toolkit :: Common Client
 * %%
 * Copyright (C) 2008 - 2017 IRD, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.collect.Lists;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.renderer.lite.RendererUtilities;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.geotools.swing.JMapPane;
import org.geotools.swing.event.MapPaneAdapter;
import org.geotools.swing.event.MapPaneEvent;


import static org.nuiton.i18n.I18n.n;
import static org.nuiton.i18n.I18n.t;

/**
 * @author Tony Chemit - [email protected]
 */
public class ObserveMapPane extends JMapPane {

    private static final long serialVersionUID = 1L;
    private static final Log log = LogFactory.getLog(ObserveMapPane.class);

    protected static final int MARGIN = 10;

    protected static final int SCALE_HEIGHT = 15;
    protected static final int SCALE_WIDTH_MAX = 200;

    protected static final int METERS_BY_MILES = 1852;

    protected int scaleWidth;

    protected String labelScaleUp;

    protected double rotation;

    public ObserveMapPane() {
        labelScaleUp = "0 m";
        scaleWidth = 100;
        rotation = 0;
        addMapPaneListener(new MapPaneAdapter() {
            @Override
            public void onDisplayAreaChanged(MapPaneEvent ev) {
                updateScale();
            }
        });
        legendItems = Lists.newArrayList();

    }


    protected void updateScale() {
        ReferencedEnvelope displayArea = getDisplayArea();
        double dpi = 2.54 / 100; // pour avoir l'echélle en metre/pixel

        try {
            double meterPerPixel = RendererUtilities.calculateScale(displayArea, getWidth(), getHeight(), dpi);

            double maxWidthMeter = SCALE_WIDTH_MAX * meterPerPixel;

            double maxWidthMiles = maxWidthMeter / METERS_BY_MILES;

            int nbDigit = (int) Math.floor(Math.log10(maxWidthMiles));

            int firstDigit = (int) Math.floor(maxWidthMiles / Math.pow(10, nbDigit));  // le premier chiffre significatif

            int useFirstDigit;

            if (firstDigit >= 5) {
                useFirstDigit = 5;
            } else if (firstDigit >= 2) {
                useFirstDigit = 2;
            } else {
                useFirstDigit = 1;
            }

            long scaleInMiles = useFirstDigit * (long) Math.pow(10, nbDigit);

            scaleWidth = (int) Math.round(scaleInMiles * METERS_BY_MILES / meterPerPixel);

            labelScaleUp = String.format("%,d " + t("observe.content.map.miles"), scaleInMiles);

        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("error", e);
            }
        }
    }

    protected void paintScale(Graphics graphics) {
        graphics.setColor(Color.BLACK);

        FontMetrics fm = graphics.getFontMetrics();

        Rectangle2D textArea = fm.getStringBounds(labelScaleUp, graphics);

        int labelLeft = getWidth() - MARGIN * 2 - scaleWidth - (int) textArea.getWidth();

        graphics.drawString(labelScaleUp, labelLeft, getHeight() - MARGIN);

        int scalesEndX = getWidth() - MARGIN;

        int scaleStartX = scalesEndX - scaleWidth;

        int scalesEndY = getHeight() - MARGIN;

        int scaleStartY = scalesEndY - SCALE_HEIGHT;

        graphics.drawLine(scaleStartX, scaleStartY, scaleStartX, scalesEndY);
        graphics.drawLine(scaleStartX, scalesEndY, scalesEndX, scalesEndY);
        graphics.drawLine(scalesEndX, scalesEndY, scalesEndX, scaleStartY);

    }

    protected static int AXIS_LENGTH = 30;

    protected static int SUB_AXIS_LENGTH = 5;

    protected static int CENTER_MARGIN = 50;

    protected static int INTER_AXIS_TEXT = 3;

    protected static double FONT_SIZE = 12;


    protected void paintCompass(Graphics graphics) {

        Point center = new Point(getWidth() - CENTER_MARGIN, CENTER_MARGIN);

        Font font = graphics.getFont();
        Font fontRatio = font.deriveFont((float) (FONT_SIZE));
        graphics.setFont(fontRatio);

        FontMetrics fm = graphics.getFontMetrics();

        for (CardinalPoint cardinalPoint : CardinalPoint.values()) {

            Point2D direction = cardinalPoint.getDirection(rotation, AXIS_LENGTH, center);

            Point2D sommet1 = cardinalPoint.getDirection(rotation - Math.PI / 4, SUB_AXIS_LENGTH, center);

            Point2D sommet2 = cardinalPoint.getDirection(rotation + Math.PI / 4, SUB_AXIS_LENGTH, center);


            Polygon polygon = new Polygon();
            polygon.addPoint((int) center.getX(), (int) center.getY());
            polygon.addPoint((int) direction.getX(), (int) direction.getY());
            polygon.addPoint((int) sommet2.getX(), (int) sommet2.getY());
            graphics.fillPolygon(polygon);

            graphics.drawLine((int) sommet1.getX(), (int) sommet1.getY(), (int) direction.getX(), (int) direction.getY());

            Rectangle2D textArea = fm.getStringBounds(cardinalPoint.getLabel(), graphics);

            // on cherche la ditance entre le centre du text et sa bordure dans le direction donné
            double l = cardinalPoint.distanceCenterBorder(rotation, textArea);

            Point2D textCenter = cardinalPoint.getDirection(rotation, AXIS_LENGTH + INTER_AXIS_TEXT + l, center);

            graphics.drawString(
                    cardinalPoint.getLabel(),
                    (int) (textCenter.getX() - textArea.getWidth() / 2),
                    (int) (textCenter.getY() + textArea.getHeight() / 2));
        }

    }


    protected static int LEGEND_MARGIN = 3;

    protected List legendItems;

    public List getLegendItems() {
        return legendItems;
    }

    public void setLegendItems(List legendItems) {
        this.legendItems = legendItems;
    }

    protected void paintLegend(Graphics graphics) {

        if (legendItems != null) {

            int x = 0;
            int y = getHeight() - ObserveMapPaneLegendItem.LEGEND_ITEM_HEIGHT * legendItems.size() - 2 * LEGEND_MARGIN;

            ObserverMapPanLegendDrawer drawer = new ObserverMapPanLegendDrawer();

            FontMetrics fm = graphics.getFontMetrics();

            int maxLabelWidth = 0;

            for (ObserveMapPaneLegendItem item : legendItems) {
                Rectangle2D labelArea = fm.getStringBounds(item.getLabel(), graphics);

                maxLabelWidth = Math.max((int) labelArea.getWidth(), maxLabelWidth);

            }

            int legendWidth = ObserveMapPaneLegendItem.LEGEND_SYMBOL_WIDTH + maxLabelWidth + ObserveMapPaneLegendItem.LEGEND_MARGIN * 2;

            graphics.setColor(ObserveMapPaneLegendItem.LEGEND_BACKGROUND);
            graphics.fillRect(
                    x,
                    y,
                    legendWidth,
                    LEGEND_MARGIN);

            y += LEGEND_MARGIN;

            for (ObserveMapPaneLegendItem item : legendItems) {

                graphics.setColor(ObserveMapPaneLegendItem.LEGEND_BACKGROUND);
                graphics.fillRect(
                        x,
                        y,
                        legendWidth,
                        ObserveMapPaneLegendItem.LEGEND_ITEM_HEIGHT);

                BufferedImage symbole = new BufferedImage(
                        ObserveMapPaneLegendItem.LEGEND_SYMBOL_WIDTH,
                        ObserveMapPaneLegendItem.LEGEND_ITEM_HEIGHT,
                        BufferedImage.TYPE_INT_ARGB);

                Style style = item.getStyle();

                for (FeatureTypeStyle featureTypeStyle : style.featureTypeStyles()) {

                    for (Rule rule : featureTypeStyle.rules()) {

                        if (rule.getFilter().evaluate(item.getSimpleFeature())) {

                            drawer.drawDirect(symbole, item.getSimpleFeature(), rule);

                        }
                    }
                }

                graphics.drawImage(symbole, x + LEGEND_MARGIN, y, null);

                graphics.setColor(Color.BLACK);

                int labelMarginBottom = ((ObserveMapPaneLegendItem.LEGEND_ITEM_HEIGHT - fm.getHeight()) / 2) + fm.getDescent();

                graphics.drawString(item.getLabel(),
                                    x + LEGEND_MARGIN + ObserveMapPaneLegendItem.LEGEND_SYMBOL_WIDTH,
                                    y + ObserveMapPaneLegendItem.LEGEND_ITEM_HEIGHT - labelMarginBottom);

                y += ObserveMapPaneLegendItem.LEGEND_ITEM_HEIGHT;

            }

            graphics.setColor(ObserveMapPaneLegendItem.LEGEND_BACKGROUND);
            graphics.fillRect(
                    x,
                    y,
                    legendWidth,
                    LEGEND_MARGIN);

        }

    }


    @Override
    public void paint(Graphics graphics) {

        super.paint(graphics);

        paintScale(graphics);

        paintCompass(graphics);

        paintLegend(graphics);

    }

    protected enum CardinalPoint {
        NORTH(-1, 0, 0, -1, n("observe.content.map.north")),
        SOUTH(1, 0, 0, 1, n("observe.content.map.south")),
        WEST(0, -1, 1, 0, n("observe.content.map.west")),
        EST(0, 1, -1, 0, n("observe.content.map.east"));

        protected int matrix00;
        protected int matrix01;
        protected int matrix10;
        protected int matrix11;
        protected String label;

        CardinalPoint(int matrix00, int matrix01, int matrix10, int matrix11, String label) {
            this.matrix00 = matrix00;
            this.matrix01 = matrix01;
            this.matrix10 = matrix10;
            this.matrix11 = matrix11;
            this.label = label;
        }

        public Point2D.Double getDirection(double angle, double length, Point center) {

            double x = Math.sin(angle) * length;
            double y = Math.cos(angle) * length;

            double deltaX = matrix00 * x + matrix01 * y;
            double deltaY = matrix10 * x + matrix11 * y;

            return new Point2D.Double(center.getX() + deltaX, center.getY() + deltaY);
        }

        // on cherche la ditance entre le centre du text et sa bordure dans le direction donné
        public double distanceCenterBorder(double angle, Rectangle2D textArea) {

            double x = Math.sin(angle);
            double y = Math.cos(angle);

            double deltaW = Math.abs(textArea.getWidth() / 2 / (matrix00 * x + matrix01 * y));
            double deltaH = Math.abs(textArea.getHeight() / 2 / (matrix10 * x + matrix11 * y));

            return Math.min(deltaH, deltaW);
        }

        public String getLabel() {
            return t(label);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy