/* * This is part of Geomajas, a GIS framework, http://www.geomajas.org/. * * Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium. * * The program is available in open source according to the GNU Affero * General Public License. All contributions in this program are covered * by the Geomajas Contributors License Agreement. For full licensing * details, see LICENSE.txt in the project root. */ package org.geomajas.gwt2.client.widget.control.scalebar; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Style; import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Widget; import org.geomajas.annotation.Api; import org.geomajas.gwt2.client.GeomajasImpl; import org.geomajas.gwt2.client.event.ViewPortChangedEvent; import org.geomajas.gwt2.client.event.ViewPortChangedHandler; import org.geomajas.gwt2.client.map.MapPresenter; import org.geomajas.gwt2.client.widget.AbstractMapWidget; /** * Map widget that shows a scale bar on the map. This widget is meant to be added to the map's widget pane (see {@link * MapPresenter#getWidgetPane()}). * * @author Pieter De Graef * @since 2.0.0 */ @Api(allMethods = true) public class Scalebar extends AbstractMapWidget { /** * Unit type to use on the scale bar. * * @author Pieter De Graef */ public enum UnitType { /** * Metric Units. Meters(m) -- Kilometers(km) */ METRIC, /** * English Units. Yards(yd) -- Miles(mi) */ ENGLISH, /** * English Units. Feet(ft) -- Miles(mi) */ ENGLISH_FOOT, /** * Coordinate Reference System Units. Units(u) */ CRS } /** * UI binder definition for the {@link Scalebar} widget. * * @author Pieter De Graef */ interface ScalebarUiBinder extends UiBinder<Widget, Scalebar> { } private static final ScalebarUiBinder UI_BINDER = GWT.create(ScalebarUiBinder.class); private static final double METERS_IN_MILE = 1609.344d; private static final double METERS_IN_YARD = 0.9144d; private static final double FEET_IN_METER = 3.2808399d; private static final int MAX_SIZE_IN_PIXELS = 125; private int[] lengths = new int[] { 1, 2, 5, 10, 25, 50, 100, 250, 500, 750, 1000, 2000, 5000, 10000, 25000, 50000, 75000, 100000, 250000, 500000, 750000, 1000000, 2000000, 5000000, 10000000 }; // position in lengths array up to where to test for yards (larger values is for miles) private static final int YARD_STARTING_POINT = 11; private UnitType unitType = UnitType.METRIC; // -- for internal use, holds the last calculated best value private int widthInUnits; // -- for internal use, holds the last calculated best value private int widthInPixels; // -- for internal use, for UnitType.ENGLISH only private boolean widthInUnitsIsMiles; @UiField protected DivElement scaleBarElement; // ------------------------------------------------------------------------ // Constructors: // ------------------------------------------------------------------------ /** * Create a new instance for the given map. * * @param mapPresenter The map presenter. */ public Scalebar(MapPresenter mapPresenter) { this(mapPresenter, GeomajasImpl.getClientBundleFactory().createScalebarResource()); } /** * Create a new instance for the given map. * * @param mapPresenter The map presenter. * @param resource Custom resource bundle in case you want to provide your own style for this widget. */ public Scalebar(MapPresenter mapPresenter, ScalebarResource resource) { super(mapPresenter); resource.css().ensureInjected(); mapPresenter.getEventBus().addViewPortChangedHandler(new ViewPortChangedHandler() { public void onViewPortChanged(ViewPortChangedEvent event) { redrawScale(); } }); initWidget(UI_BINDER.createAndBindUi(this)); redrawScale(); } // ------------------------------------------------------------------------ // Public methods: // ------------------------------------------------------------------------ /** * Get the unit type used in this widget. * * @return The unit type in which scales are displayed. */ public UnitType getUnitType() { return unitType; } /** * Set the unit type in which scales are displayed. * * @param unitType The new unit type. */ public void setUnitType(UnitType unitType) { this.unitType = unitType; } // ------------------------------------------------------------------------ // Private methods: // ------------------------------------------------------------------------ private void redrawScale() { calculateBestFit(1 / mapPresenter.getViewPort().getResolution()); scaleBarElement.setInnerText(formatUnits(widthInUnits)); scaleBarElement.getStyle().setWidth(widthInPixels, Style.Unit.PX); getElement().getStyle().setWidth(widthInPixels + 10, Style.Unit.PX); } /** * Find the rounded value (from the lengths array) which fits the closest into the maxSizeInPixels for the given * scale. * * @param scale * @return closest fit in units (will be miles or yards for English, m for metric, unit for CRS) */ private void calculateBestFit(double scale) { double unitLength = mapPresenter.getConfiguration().getUnitLength(); int len = 0; long px = 0; if (UnitType.ENGLISH.equals(unitType)) { // try miles. for (int i = lengths.length - 1; i > -1; i--) { len = this.lengths[i]; px = Math.round((len * scale / unitLength) * METERS_IN_MILE); if (px < MAX_SIZE_IN_PIXELS) { break; } } // try yards. if (px > MAX_SIZE_IN_PIXELS) { for (int i = YARD_STARTING_POINT; i > -1; i--) { len = this.lengths[i]; px = Math.round((len * scale / unitLength) * METERS_IN_YARD); if (px < MAX_SIZE_IN_PIXELS) { break; } } widthInUnitsIsMiles = false; } else { widthInUnitsIsMiles = true; } } else if (UnitType.ENGLISH_FOOT.equals(unitType)) { // try miles. for (int i = lengths.length - 1; i > -1; i--) { len = this.lengths[i]; px = Math.round((len * scale / unitLength) * METERS_IN_MILE); if (px < MAX_SIZE_IN_PIXELS) { break; } } // try feet. if (px > MAX_SIZE_IN_PIXELS) { for (int i = YARD_STARTING_POINT; i > -1; i--) { len = this.lengths[i]; px = Math.round((len * scale / unitLength) / FEET_IN_METER); if (px < MAX_SIZE_IN_PIXELS) { break; } } widthInUnitsIsMiles = false; } else { widthInUnitsIsMiles = true; } } else { for (int i = lengths.length - 1; i > -1; i--) { len = this.lengths[i]; px = Math.round(len * scale / unitLength); if (px < MAX_SIZE_IN_PIXELS) { break; } } } widthInUnits = len; widthInPixels = (int) px; } /** * format to human readable string converting to unit type. * * @param units * @return */ private String formatUnits(int units) { switch (unitType) { case ENGLISH: return NumberFormat.getDecimalFormat().format(units) + (widthInUnitsIsMiles ? " mi" : " yd"); case ENGLISH_FOOT: return NumberFormat.getDecimalFormat().format(units) + (widthInUnitsIsMiles ? " mi" : " ft"); case METRIC: if (units < 10000) { return NumberFormat.getDecimalFormat().format(units) + " m"; } else { return NumberFormat.getDecimalFormat().format((double) units / 1000) + " km"; } case CRS: return NumberFormat.getDecimalFormat().format(units) + " u"; default: return "??"; } } }