Package org.locationtech.udig.mapgraphic.scalebar

Source Code of org.locationtech.udig.mapgraphic.scalebar.ScalebarMapGraphic

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2008, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.mapgraphic.scalebar;

import static org.locationtech.udig.mapgraphic.scalebar.Unit.CENTIMETER;
import static org.locationtech.udig.mapgraphic.scalebar.Unit.KILOMETER;
import static org.locationtech.udig.mapgraphic.scalebar.Unit.METER;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.text.MessageFormat;
import java.util.Locale;

import org.locationtech.udig.catalog.util.CRSUtil;
import org.locationtech.udig.core.IProviderWithParam;
import org.locationtech.udig.core.Pair;
import org.locationtech.udig.mapgraphic.MapGraphic;
import org.locationtech.udig.mapgraphic.MapGraphicContext;
import org.locationtech.udig.mapgraphic.internal.Messages;
import org.locationtech.udig.mapgraphic.scalebar.BarStyle.BarType;
import org.locationtech.udig.mapgraphic.style.FontStyle;
import org.locationtech.udig.mapgraphic.style.FontStyleContent;
import org.locationtech.udig.mapgraphic.style.LocationStyleContent;
import org.locationtech.udig.project.IStyleBlackboard;
import org.locationtech.udig.project.ProjectBlackboardConstants;
import org.locationtech.udig.project.render.displayAdapter.IMapDisplay;
import org.locationtech.udig.ui.graphics.ViewportGraphics;

/**
* Provides a decorator object that represents a typical Scalebar
*
* @author Richard Gould
* @since 0.6.0
*/
public class ScalebarMapGraphic implements MapGraphic {

    private static final String NUMBER_PATTERN = "{0,number,integer} "; //$NON-NLS-1$

    /** The width of the lines draw in the scalebar */
    private static final int BAR_WIDTH = 1;
    /** The height of the scale bar */
    private static final int BAR_HEIGHT = 8;

    /**
     * Finds a "nice" number to display for the given measurement
     *
     * @param idealDistance the optimal distance in any unit.
     * @param range a strategy for obtaining "Good"/"attractive values. The first time the method is
     *        called a value of -1 will be passed to the parameter. Following calls will pass the
     *        result of the previous call. If the object returns null then it will signal that there
     *        are no more good values. This will be to obtain the next value. The provider should
     *        not contain state.
     * @return one of the values provided by the strategy. Always is smaller than idealDistance
     */
    static int closestInt( int idealDistance, final IProviderWithParam<Integer, Integer> range ) {

        if (range == null) {
            throw new NullPointerException("Range cannot be null"); //$NON-NLS-1$
        }

        int result = -1;
        Integer next = range.get(-1);
        Integer previous = -1;
        while( next != null ) {
            // perfect match lets take this
            if (next == idealDistance) {
                result = next;
                break;
            }

            // the current value is too high so lets take the previous.
            // this is so it will always display.
            // if this sucks then choose a better provider strategy
            if (idealDistance < next) {
                result = previous;
                break;
            }
            previous = next;
            next = range.get(next);
        }
        return result;
    }
    /*
     * @see org.locationtech.udig.project.render.decorator.Decorator#draw(java.awt.Graphics2D,
     *      org.locationtech.udig.project.render.RenderContext)
     */
    public void draw( MapGraphicContext context ) {
        // draw1(context);
        drawScaleDenom(context);
    }

    private void drawScaleDenom( MapGraphicContext context ) {
        double scaleDenom = context.getViewportModel().getScaleDenominator();
        IMapDisplay display = context.getMapDisplay();
        int dpi = display.getDPI(); // pixels per inch
        int displayHeight = display.getHeight();

        double i = 1 / (double) dpi; // invert to get in per pixel
        double pixelInMeters = i * .0254; // to meters per pixel

        double inMeters = scaleDenom * pixelInMeters;

        Rectangle location = getGraphicLocation(context);
       
        // reserve this area of the screen so labels are not drawn here
        context.getLabelPainter().put( location );

        if (inMeters == 0.0) {
            drawWarning(context.getGraphics(), location, displayHeight);
        } else {
            BarStyle type = getBarStyle(context);
            UnitPolicy scalebarUnits = type.getUnits();
            if ((scalebarUnits == UnitPolicy.AUTO) && (CRSUtil.isCoordinateReferenceSystemImperial(context.getCRS()))){
                scalebarUnits = UnitPolicy.IMPERIAL;
            }
            Pair<Integer, Pair<Integer, Unit>> result2 = null;
            if (scalebarUnits == UnitPolicy.IMPERIAL){
                result2 = calculateUnitAndLength(inMeters,
                        location.width / type.getNumintervals(), Unit.MILE, Unit.FOOT, Unit.YARD, Unit.INCHES);
            }else{ /* METRIC and AUTO both treated as metric, since auto is converted above in CRS search */
                result2 = calculateUnitAndLength(inMeters,
                        location.width / type.getNumintervals(), KILOMETER, METER, CENTIMETER);               
            }
           
            int trueBarLength2 = result2.getLeft() * type.getNumintervals();
            Pair<Integer, Unit> unitMeasure2 = result2.getRight();
            int nice2 = unitMeasure2.getLeft();
            Unit measurement2 = unitMeasure2.getRight();
            doDraw(measurement2, context, trueBarLength2, nice2);

        }

    }

    private Rectangle getGraphicLocation( MapGraphicContext context ) {
        IStyleBlackboard styleBlackboard = context.getLayer().getStyleBlackboard();
        Rectangle location = (Rectangle) styleBlackboard.get(
                LocationStyleContent.ID);
        if (location == null) {
            location = LocationStyleContent.createDefaultStyle();
            styleBlackboard.put(LocationStyleContent.ID, location);
        }
        return location;
    }

    private FontStyle getFontStyle( MapGraphicContext context ) {
        IStyleBlackboard styleBlackboard = context.getLayer().getStyleBlackboard();
        FontStyle style = (FontStyle) styleBlackboard.get(FontStyleContent.ID);
        if (style == null) {
            style = new FontStyle();
            styleBlackboard.put(FontStyleContent.ID, style);
        }
        return style;
    }

    private BarStyle getBarStyle( MapGraphicContext context ) {
        IStyleBlackboard styleBlackboard = context.getLayer().getStyleBlackboard();
        BarStyle style = (BarStyle) styleBlackboard.get(BarStyleContent.ID);
        if (style == null) {
            style = new BarStyle();
            styleBlackboard.put(BarStyleContent.ID, style);
        }
        return style;

    }

    /**
     * Calculate the size of the scalebar to be drawn and the unit and actual distance given that
     * size. The calculation is based on the size of the MapGraphic location (style) and rounding to
     * a "good" size.
     *
     * @param meterPerPixel distance per pixel in meters
     * @param idealBarLength the preferred length of the bar in pixels
     * @param units =
     * @return Pair< NumberOfPixeslToDraw, Pair<GroundDistance, Unit>>
     */
    static Pair<Integer, Pair<Integer, Unit>> calculateUnitAndLength( double meterPerPixel,
            int idealBarLength, Unit... units ) {

        if (units == null || units.length == 0) {
            throw new IllegalArgumentException("units must have at least one unit"); //$NON-NLS-1$
        }

        final double mIdealBarDistance = (meterPerPixel * idealBarLength);
        final NiceIntegers niceIntegers = new NiceIntegers();

        Unit unit = units[0];
        double displayDistance = -1;
        int barLengthPixels = idealBarLength;

        for( int i = 0; displayDistance < 1 && i < units.length; i++ ) {
            unit = units[i];
            displayDistance = closestInt((int) unit.meterToUnit(mIdealBarDistance), niceIntegers);

            barLengthPixels = (int) (unit.unitToMeter(displayDistance) / meterPerPixel);
        }

        Pair<Integer, Unit> unitMeasure = new Pair<Integer, Unit>((int) displayDistance, unit);

        return new Pair<Integer, Pair<Integer, Unit>>(barLengthPixels, unitMeasure);
    }

    private void doDraw( Unit measurement, MapGraphicContext context, int trueBarLength, int nice ) {

        Rectangle location = getGraphicLocation(context);
       
        context.getLabelPainter().put( location );
       
        BarStyle type = getBarStyle(context);
        /*
         * Draw the scale bar
         */
        int x = location.x;
        int y = location.y;

        int width = trueBarLength;
        int height = location.height;

        if (x < 0) {
            x = context.getMapDisplay().getWidth() + x - trueBarLength;
        }
        if (y < 0) {
            y = context.getMapDisplay().getHeight() + y - height;
        }

        int barWidth = -1;

        //get background color
        Color bcolor = (Color) context.getMap().getBlackboard().get(
                ProjectBlackboardConstants.MAP__BACKGROUND_COLOR);
        BarStyle barStyle = getBarStyle(context);
        if (barStyle.getBgColor() != null){
            bcolor = barStyle.getBgColor();
        }
        if (bcolor == null) {
            bcolor = Color.WHITE;
        }
       
        // setup font
        FontStyle font = getFontStyle(context);
        if (font != null && font.getFont() != null) {
            context.getGraphics().setFont(font.getFont());
        }
       
        //draw halo
        Color maskColor = new Color(bcolor.getRed(), bcolor.getGreen(), bcolor.getBlue(), 80);
        computeAndDrawHalo(nice, context, measurement, type, x, y, width, height, maskColor);
       
        //draw bar with labels
        if (type.getType() == BarType.SIMPLE) {
            drawSimpleBar(context.getGraphics(), location, x, y, width, height, type
                    .getColor());
            drawSingleLabel(context, measurement, nice * type.getNumintervals(), x, y, height,
                    barWidth, width, type.getColor());
        } else if (type.getType() == BarType.SIMPLE_LINE) {
            drawLineBar(context.getGraphics(), location, x, y, width, height, type
                    .getNumintervals(), type.getColor(), bcolor);
            drawIntervalLabels(context, measurement, nice, x, y, height, barWidth, width, type
                    .getNumintervals(), type.getColor());
        } else if (type.getType() == BarType.FILLED) {
             drawFilled(context.getGraphics(), location, x, y, width, height, type
                    .getNumintervals(), type.getColor(), bcolor);
            drawIntervalLabels(context, measurement, nice, x, y, height, barWidth, width, type
                    .getNumintervals(), type.getColor());
        } else if (type.getType() == BarType.FILLED_LINE) {
             drawFilledLineBar(context.getGraphics(), location, x, y, width, height, type
                    .getNumintervals(), type.getColor(), bcolor);
            drawIntervalLabels(context, measurement, nice, x, y, height, barWidth, width, type
                    .getNumintervals(), type.getColor());
        }
    }

    private void computeAndDrawHalo( int nice, MapGraphicContext context, Unit measurement,
            BarStyle type, int x, int y, int width, int height, Color bgColor ) {

        // determine maximum label size
        MessageFormat formatter = new MessageFormat("", Locale.getDefault()); //$NON-NLS-1$
        formatter.applyPattern(NUMBER_PATTERN + measurement.display);
        Object[] arguments = {nice * type.getNumintervals()};
        String msg = formatter.format(arguments);
        Rectangle2D msgBounds = context.getGraphics().getStringBounds(msg);

        double textwidth = msgBounds.getWidth();
        double textheight = msgBounds.getHeight();

        if (type.getType() == BarType.SIMPLE){
            textwidth = 10;
        }
        double halox = (int) (x + BAR_WIDTH) - (textwidth / 2.0);
        double halowidth = ((int) (width / (type.getNumintervals() * 2))) * type.getNumintervals()
                * 2 + (textwidth) + 5;

        double haloy = y + height - BAR_HEIGHT - textheight - 2;
        double haloheight = BAR_HEIGHT + textheight + 4;

        context.getGraphics().setColor(bgColor);
        RoundRectangle2D roundBounds = new RoundRectangle2D.Double();
        roundBounds.setRoundRect(halox, haloy, halowidth, haloheight, 6, 6);
        context.getGraphics().fill(roundBounds);
    }
    /**
     * draws a single label for the simple scalebar
     */
    private void drawSingleLabel( MapGraphicContext context, Unit measurement, int nice, int x,
            int y, int height, int barWidth, int width, Color c ) {
        ViewportGraphics graphics = context.getGraphics();

        FontStyle font = getFontStyle(context);

        if (font != null && font.getFont() != null) {
            graphics.setFont(font.getFont());
        }

        MessageFormat formatter = new MessageFormat("", Locale.getDefault()); //$NON-NLS-1$
        formatter.applyPattern(NUMBER_PATTERN + measurement.display);

        Object[] arguments = {nice};
        String msg = formatter.format(arguments);
        Rectangle2D msgBounds = graphics.getStringBounds(msg);

        int yText = y + (height - barWidth) - 4;
        int xText = x + (int) (width * 0.5) - (int) (msgBounds.getWidth() * 0.5);

        // Draw the string denoting kilometres (or metres)
        graphics.setColor(c);
        graphics.drawString(msg, xText, yText, ViewportGraphics.ALIGN_LEFT,
                ViewportGraphics.ALIGN_BOTTOM);
    }

    /**
     * draws the interval labels
     */
    private void drawIntervalLabels( MapGraphicContext context, Unit measurement, int nice, int x,
            int y, int height, int barWidth, int width, int numIntervals, Color c ) {

        ViewportGraphics graphics = context.getGraphics();

        FontStyle font = getFontStyle(context);

        if (font != null && font.getFont() != null) {
            graphics.setFont(font.getFont());
        }

        MessageFormat formatter = new MessageFormat("", Locale.getDefault()); //$NON-NLS-1$
        formatter.applyPattern(NUMBER_PATTERN);

        for( int i = -1; i < numIntervals - 1; i++ ) {

            drawIntervalLabel(nice, x, y, height, barWidth, width, numIntervals, c,
                    graphics, formatter, i);
        }

        formatter = new MessageFormat("", Locale.getDefault()); //$NON-NLS-1$
        formatter.applyPattern(NUMBER_PATTERN + measurement.display);
        drawIntervalLabel(nice, x, y, height, barWidth, width, numIntervals, c,
                graphics, formatter, numIntervals - 1);
    }

    /**
     * draws an individual interval label with a background halo
     */
    private void drawIntervalLabel( int nice, int x, int y, int height, int barWidth, int width,
            int numIntervals, Color c, ViewportGraphics graphics,
            MessageFormat formatter, int i ) {
        Object[] arguments = {nice * i};
        String msg = formatter.format(arguments);
        msg = msg.trim();
        Rectangle2D msgBounds = graphics.getStringBounds(msg);

        int yText = y + (height - barWidth) - 12;
        int xText = x + barWidth + (i + 1) * (width / numIntervals)
                - ((int) msgBounds.getWidth() / 2);

        // Draw the string denoting kilometres (or metres)
        graphics.setColor(c);
        graphics.drawString(msg, xText, yText, ViewportGraphics.ALIGN_LEFT,
                ViewportGraphics.ALIGN_BOTTOM);
    }

    /**
     * Draws a bar from the start to the end with both ends included:
     *
     * <pre><code>
     * |                                              |
     * +----------------------------------------------+
     * </code></pre>
     */
    private void drawSimpleBar( ViewportGraphics graphics, Rectangle location, int x, int y,
            int width, int height, Color c ) {

        int barStartX = x + 1 + BAR_WIDTH / 2;
        int barStartY = y - 2 + location.height - BAR_HEIGHT;

        GeneralPath path = new GeneralPath();
        path.moveTo(barStartX, barStartY);
        path.lineTo(barStartX, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + width, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + width, barStartY);

        graphics.setColor(c);
        graphics.setStroke(ViewportGraphics.LINE_SOLID, BAR_WIDTH);
        graphics.draw(path);
    }

    /**
     * Draws a scale bar with every second scale filled in.
     *
     * <pre><code>
     * +--+--+----+----+----+----+
     * |xx|  |xxxx|    |xxxx|    |
     * +--+--+----+----+----+----+
     * </code></pre>
     */
    private void drawFilled( ViewportGraphics graphics, Rectangle location, int x, int y,
            int width, int height, int numIntervals, Color c, Color background ) {

        int barStartX = x + 1 + BAR_WIDTH / 2;
        int barStartY = y - 2 + location.height - BAR_HEIGHT;

        int interval = width / (numIntervals * 2);

        Color color = c;
        Color intervalColor = background;

        graphics.setColor(color);
        graphics.fillRect(barStartX, barStartY, (int) interval, BAR_HEIGHT);
        for( int i = 2; i < numIntervals * 2; i += 4 ) {
            graphics.fillRect(barStartX + i * interval, barStartY, interval * 2, BAR_HEIGHT);
        }

        graphics.setColor(intervalColor);
        graphics.fillRect(barStartX + interval, barStartY, interval, BAR_HEIGHT);
        for( int i = 4; i < numIntervals * 2; i += 4 ) {
            graphics.fillRect(barStartX + i * interval, barStartY, interval * 2, BAR_HEIGHT);
        }

        // draw border around the whole thing
        GeneralPath path = new GeneralPath();
        path.moveTo(barStartX, barStartY);

        path.lineTo(barStartX, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + numIntervals * 2 * interval, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + numIntervals * 2 * interval, barStartY);
        path.lineTo(barStartX, barStartY);

        graphics.setColor(color);
        graphics.setStroke(ViewportGraphics.LINE_SOLID, BAR_WIDTH);
        graphics.draw(path);
    }

    /**
     * Draws a bar with lines representing breaks:
     *
     * <pre><code>
     * +--+--+----+----+----+----+
     * |  |  |    |    |    |    |
     * +--+  +----+    +----+    +
     * |  |  |    |    |    |    |
     * +--+--+----+----+----+----+
     * </code></pre>
     */
    private void drawLineBar( ViewportGraphics graphics, Rectangle location, int x, int y,
            int width, int height, int numIntervals, Color c, Color bcolor ) {

        int barStartX = x + 1 + BAR_WIDTH / 2;
        int barStartY = y - 2 + location.height - BAR_HEIGHT;

        int interval = width / (numIntervals * 2);

        //draw solid background
        graphics.setColor(bcolor);
        graphics.fillRect(barStartX, barStartY, (int) interval * numIntervals * 2, BAR_HEIGHT);
       
       
        GeneralPath path = new GeneralPath();
        // draw outline box
        path.moveTo(barStartX, barStartY);
        path.lineTo(barStartX, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + numIntervals * 2 * interval, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + numIntervals * 2 * interval, barStartY);
        path.lineTo(barStartX, barStartY);

        // draw vertical dividers
        path.moveTo(barStartX + interval, barStartY);
        path.lineTo(barStartX + interval, barStartY + BAR_HEIGHT);
        for( int i = 2; i < numIntervals * 2; i += 2 ) {
            path.moveTo(barStartX + i * interval, barStartY);
            path.lineTo(barStartX + i * interval, barStartY + BAR_HEIGHT);
        }

        // draw horizontal dividers
        path.moveTo(barStartX, barStartY + BAR_HEIGHT / 2);
        path.lineTo(barStartX + interval, barStartY + BAR_HEIGHT / 2);
        for( int i = 2; i < numIntervals * 2; i += 4 ) {
            path.moveTo(barStartX + i * interval, barStartY + BAR_HEIGHT / 2);
            path.lineTo(barStartX + (i + 2) * interval, barStartY + BAR_HEIGHT / 2);
        }

        graphics.setColor(c);
        graphics.setStroke(ViewportGraphics.LINE_SOLID, BAR_WIDTH);
        graphics.draw(path);
    }

    /**
     * Draws a bar with lines representing breaks & alternate filled breaks:
     *
     * <pre><code>
     * +--+--+----+----+----+----+
     * |xx|  |xxxx|    |xxxx|    |
     * +--+--+----+----+----+----+
     * |  |xx|    |xxxx|    |xxxx|
     * +--+--+----+----+----+----+
     * </pre></code>
     */
    private void drawFilledLineBar( ViewportGraphics graphics, Rectangle location, int x, int y,
            int width, int height, int numIntervals, Color c, Color background ) {

        int barStartX = x + 1 + BAR_WIDTH / 2;
        int barStartY = y - 2 + location.height - BAR_HEIGHT;

        int interval = width / (numIntervals * 2);

        Color color = c;
        Color intervalColor = background;


        graphics.setColor(color);
        graphics.fillRect(barStartX, barStartY, interval, BAR_HEIGHT / 2);
        graphics.fillRect((barStartX + interval), barStartY + BAR_HEIGHT / 2, interval,
                BAR_HEIGHT / 2);
        for( int i = 2; i < numIntervals * 2; i += 2 ) {
            if (i % 4 != 0) {
                graphics.fillRect(barStartX + i * interval, barStartY, interval * 2, BAR_HEIGHT / 2);
            } else {
                graphics.fillRect(barStartX + i * interval, barStartY + BAR_HEIGHT / 2,
                        (int) (interval * 2), BAR_HEIGHT / 2);
            }
        }

        graphics.setColor(intervalColor);
        graphics.fillRect(barStartX, barStartY + BAR_HEIGHT / 2, interval, BAR_HEIGHT / 2);
        graphics.fillRect(barStartX + interval, barStartY, interval, BAR_HEIGHT / 2);
        for( int i = 2; i < numIntervals * 2; i += 2 ) {
            if (i % 4 != 0) {
                graphics.fillRect(barStartX + i * interval, barStartY + BAR_HEIGHT / 2,
                        interval * 2, BAR_HEIGHT / 2);
            } else {
                graphics.fillRect(barStartX + i * interval, barStartY, interval * 2, BAR_HEIGHT / 2);
            }
        }

        GeneralPath path = new GeneralPath();
        path.moveTo(barStartX, barStartY);
        path.lineTo(barStartX, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + numIntervals * 2 * interval, barStartY + BAR_HEIGHT);
        path.lineTo(barStartX + numIntervals * 2 * interval, barStartY);
        path.lineTo(barStartX, barStartY);

        path.moveTo(barStartX + interval, barStartY);
        path.lineTo(barStartX + interval, barStartY + BAR_HEIGHT);

        for( int i = 2; i < numIntervals * 2; i += 2 ) {
            path.moveTo(barStartX + i * interval, barStartY);
            path.lineTo(barStartX + i * interval, barStartY + BAR_HEIGHT);

        }

        graphics.setColor(color);
        graphics.setStroke(ViewportGraphics.LINE_SOLID, BAR_WIDTH);
        graphics.draw(path);
    }

    private void drawWarning( ViewportGraphics graphics, Rectangle location, int displayHeight ) {
        graphics.setColor(Color.GRAY);
        graphics.fillRect(location.x, location.y, location.width, location.height);
        graphics.setColor(Color.BLACK);
        // graphics.drawRect( location.x, location.y, location.width, location.height );
        graphics.draw(new Rectangle(location.x, location.y, location.width, location.height));

        int yText = (location.y < (displayHeight / 2))
                ? location.y + location.height + 15
                : location.y - 15;
        graphics.drawString(Messages.ScalebarMapGraphic_zoomInRequiredMessage, location.x, yText,
                -1, -1);
    }

}
TOP

Related Classes of org.locationtech.udig.mapgraphic.scalebar.ScalebarMapGraphic

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.