Package org.geomajas.gwt.client.widget

Source Code of org.geomajas.gwt.client.widget.ScaleSelect$ScaleValidator

/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 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.gwt.client.widget;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;

import org.geomajas.annotation.Api;
import org.geomajas.gwt.client.i18n.I18nProvider;
import org.geomajas.gwt.client.map.MapView;
import org.geomajas.gwt.client.map.event.MapModelEvent;
import org.geomajas.gwt.client.map.event.MapModelHandler;
import org.geomajas.gwt.client.map.event.MapViewChangedEvent;
import org.geomajas.gwt.client.map.event.MapViewChangedHandler;

import com.google.gwt.i18n.client.NumberFormat;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import com.smartgwt.client.widgets.form.fields.events.KeyPressEvent;
import com.smartgwt.client.widgets.form.fields.events.KeyPressHandler;
import com.smartgwt.client.widgets.form.validator.CustomValidator;

/**
* <p>
* A drop down selection box for setting and displaying the current scale of a map. The displayed scale is a relative
* scale and is expressed as an abstract number, as opposed to the MapView scale which is expressed in pixels per map
* unit. An arbitrary scale can be filled in by the user. Implemented as a canvas so it can be added where needed.
* </p>
* <p>
* This widget has the option to add new scale levels to the list, as the user types them (and presses 'Enter'). By
* default this option is turned off. Turn it on using the <code>setUpdatingScaleList</code> method.
* </p>
*
* @author Jan De Moerloose
* @since 1.6.0
*/
@Api
public class ScaleSelect extends Canvas implements KeyPressHandler, ChangedHandler, MapViewChangedHandler {

  private MapView mapView;

  private ComboBoxItem scaleItem;

  private static NumberFormat DENOMINATOR_FORMAT = NumberFormat.getFormat("###,###");

  // bidirectional lookup
  private LinkedHashMap<Double, String> scaleToValue;

  private LinkedHashMap<String, Double> valueToScale;

  // pixel length in map units
  private double pixelLength;

  private boolean updatingScaleList;

  // Full list of scales. These will not always be shown (depends on map size and maximum bounds), but they are
  // stored here for when the map resizes, and these must all of a sudden become available.
  private List<Double> scaleList;

  // -------------------------------------------------------------------------
  // Constructor:
  // -------------------------------------------------------------------------

  /**
   * Constructs a ScaleSelect that acts on the specified map view.
   *
   * @param mapView
   *            the map view
   * @param pixelLength
   *            the pixel length in map units
   * @since 1.6.0
   */
  @Api
  public ScaleSelect(MapView mapView, double pixelLength) {
    this.mapView = mapView;
    this.pixelLength = pixelLength;
    setWidth100();
    setHeight100();
    init();
  }

  /**
   * Constructs a ScaleSelect that acts on the specified map view.
   *
   * @param mapWidget map widget
   * @since 1.10.0
   */
  @Api
  public ScaleSelect(final MapWidget mapWidget) {
    this(mapWidget.getMapModel().getMapView(), mapWidget.getPixelPerUnit());
    mapWidget.getMapModel().addMapModelHandler(new MapModelHandler() {
      public void onMapModelChange(MapModelEvent event) {
        pixelLength = mapWidget.getPixelPerUnit();
        updateResolutions();
      }
    });
  }

  // -------------------------------------------------------------------------
  // Public methods:
  // -------------------------------------------------------------------------

  /**
   * Set the specified relative scale values in the select item.
   *
   * @param scales
   *            array of relative scales (should be multiplied by pixelLength if in pixels/m)
   * @since 1.6.0
   */
  @Api
  public void setScales(Double... scales) {
    // Sort decreasing and store the list:
    Arrays.sort(scales, Collections.reverseOrder());
    scaleList = Arrays.asList(scales);

    // Create lookup maps (stores user friendly representation):
    scaleToValue = new LinkedHashMap<Double, String>();
    valueToScale = new LinkedHashMap<String, Double>();
    for (Double scale : scales) {
      // Eliminate duplicates and null:
      if (scale != null && !scaleToValue.containsKey(scale)) {
        String value = scaleToString(scale);
        scaleToValue.put(scale, value);
        valueToScale.put(value, scale);
      }
    }

    // Apply the requested scales on the SelectItem. Make sure only available scales are added:
    updateScaleList();
  }

  /**
   * Return the {@link MapView} object to which this widget is connected.
   *
   * @return {@link MapView} object which is connected to this widget
   */
  public MapView getMapView() {
    return mapView;
  }

  /**
   * When typing custom scale levels in the select item, should these new scale levels be added to the list or not?
   *
   * @return true when new (typed) scales should be added in list
   */
  public boolean isUpdatingScaleList() {
    return updatingScaleList;
  }

  /**
   * When typing custom scale levels in the select item, should these new scale levels be added to the list or not?
   * The default value is false, which means that the list of scales in the select item does not change.
   *
   * @param updatingScaleList
   *            Should the new scale be added?
   */
  public void setUpdatingScaleList(boolean updatingScaleList) {
    this.updatingScaleList = updatingScaleList;
  }

  /** When the MapView changes, update the select item to the correct scale. */
  public void onMapViewChanged(MapViewChangedEvent event) {
    if (scaleItem.getValueAsString() == null || "".equals(scaleItem.getValueAsString())) {
      setDisplayScale(mapView.getCurrentScale() * pixelLength);
    }

    if (event.isMapResized()) {
      updateScaleList();
      setDisplayScale(mapView.getCurrentScale() * pixelLength);
    } else if (!event.isSameScaleLevel() || scaleItem.getDisplayValue() == null
        || "".equals(scaleItem.getDisplayValue())) {
      setDisplayScale(mapView.getCurrentScale() * pixelLength);
    }
  }

  /**
   * Make sure that the scale in the scale select is applied on the map, when the user presses the 'Enter' key.
   */
  public void onKeyPress(KeyPressEvent event) {
    String name = event.getKeyName();
    if (name.equalsIgnoreCase("enter")) {
      reorderValues();
    }
  }

  /**
   * When the user selects a different scale, have the map zoom to it.
   */
  public void onChanged(ChangedEvent event) {
    String value = (String) scaleItem.getValue();
    Double scale = valueToScale.get(value);
    if (scale != null) {
      mapView.setCurrentScale(scale / pixelLength, MapView.ZoomOption.LEVEL_CLOSEST);
    }
  }

  // -------------------------------------------------------------------------
  // Private methods:
  // -------------------------------------------------------------------------

  protected void setDisplayScale(double scale) {
    scaleItem.setValue(scaleToString(scale));
  }

  protected String scaleToString(double scale) {
    if (scale > 0 && scale < 1.0) {
      int denominator = (int) Math.round(1. / scale);
      return "1 : " + DENOMINATOR_FORMAT.format(denominator);
    } else if (scale >= 1.0) {
      int denominator = (int) Math.round(scale);
      return DENOMINATOR_FORMAT.format(denominator) + " : 1";
    } else {
      return "Negative or zero scale not allowed, did you use a correct pixel length?";
    }
  }

  protected Double stringToScale(String s) {
    String[] scale2 = s.split(":");
    if (scale2.length == 1) {
      return 1.0 / DENOMINATOR_FORMAT.parse(scale2[0].trim());
    } else {
      return DENOMINATOR_FORMAT.parse(scale2[0].trim()) / DENOMINATOR_FORMAT.parse(scale2[1].trim());
    }
  }

  /**
   * Given the full list of desirable resolutions, which ones are actually available? Update the widget accordingly.
   */
  private void updateScaleList() {
    List<String> availableScales = new ArrayList<String>();

    for (Double scale : scaleList) {
      if (mapView.isResolutionAvailable(pixelLength / scale)) {
        availableScales.add(scaleToValue.get(scale));
      }
    }

    scaleItem.setValueMap(availableScales.toArray(new String[availableScales.size()]));
  }

  private void init() {
    DynamicForm form = new DynamicForm();
    scaleItem = new ComboBoxItem();
    scaleItem.setTitle(I18nProvider.getToolbar().scaleSelect());
    scaleItem.setValidators(new ScaleValidator());
    scaleItem.setValidateOnChange(true);
    scaleItem.addKeyPressHandler(this);
    scaleItem.addChangedHandler(this);
    form.setFields(scaleItem);
    addChild(form);
    updateResolutions();
    mapView.addMapViewChangedHandler(this);
  }

  private void updateResolutions() {
    if (mapView.getResolutions() != null) {
      List<Double> scales = new ArrayList<Double>();
      for (Double resolution : mapView.getResolutions()) {
        scales.add(pixelLength / resolution);
      }
      setScales(scales.toArray(new Double[scales.size()]));
    }
  }

  private void reorderValues() {
    String value = (String) scaleItem.getValue();
    if (value != null) {
      Double scale = valueToScale.get(value);
      if (scale == null) {
        scale = stringToScale(value);
        if (updatingScaleList) {
          List<Double> newScales = new ArrayList<Double>(valueToScale.values());
          newScales.add(scale);
          setScales(newScales.toArray(new Double[newScales.size()]));
        }
      }
      scaleItem.setValue(scaleToValue.get(scale));
      mapView.setCurrentScale(scale / pixelLength, MapView.ZoomOption.LEVEL_CLOSEST);
    }
  }

  /**
   * Custom validation of user entered scale
   */
  private class ScaleValidator extends CustomValidator {

    @Override
    protected boolean condition(Object value) {
      try {
        Double d = stringToScale((String) value);
        return d.doubleValue() >= 0.0;
      } catch (Throwable t) {
        return false;
      }
    }
  }
}
TOP

Related Classes of org.geomajas.gwt.client.widget.ScaleSelect$ScaleValidator

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.