Package org.zkoss.gmaps

Source Code of org.zkoss.gmaps.Gpolyline

/* Gpolyline.java

{{IS_NOTE
  Purpose:
   
  Description:
   
  History:
    Aug 9, 2007 4:38:11 PM, Created by henrichen
}}IS_NOTE

Copyright (C) 2007 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
  This program is distributed under GPL Version 2.0 in the hope that
  it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/

package org.zkoss.gmaps;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.zkoss.gmaps.event.PathChangeEvent;
import org.zkoss.lang.Objects;
import org.zkoss.util.CollectionsX;
import org.zkoss.xel.fn.StringFns;
import org.zkoss.zk.au.AuRequest;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.impl.XulElement;
/**
* Google Maps support Gpolyline.
*
* @author henrichen
* @since 2.0_6
*/
public class Gpolyline extends XulElement implements Mapitem {
  private static final long serialVersionUID = 200807041530L;
  protected List _path = new LinkedList();
  private String _strpath;
  private String _color = "#808080"; //default to dark gray
  private int _weight = 5//default to 5
  private int _opacity = 50; //default to 50/100
  private int _numLevels = 4; //default to 4
  private int _zoomFactor = 32; //zoom factor between polyline levels (2^5 if _numLevels == 4).
  protected String _encodedPolyline; //cached value
  private boolean _editable = false;
  //private boolean _smartUpdatePoly; //whether post the smartUpdate event already?
 
  static {
    addClientEvent(Gpolyline.class, "onPathChange", CE_IMPORTANT|CE_DUPLICATE_IGNORE);
  }

  public Gpolyline() {
    /*
    addEventListener("onSmartUpdatePoly", new EventListener() {
      public void onEvent(Event evt) {
        _smartUpdatePoly = false; //reset
        smartRerender();
      }
    });
    */
  }
  /**
   * Add polyline axix point.
   * <p>Note the polyline "level" does not correspond directly to a zoom level but they are related.
   * More accurately, the numLevels parameter divides up the existing zoom levels (currently 18)
   * into groups of zoom levels and polyline level is confined by the numLevels. For example, the
   * default numLevels value is 4, so the polyline level can be 0 to 3 only. The Google maps zoom
   * levels are grouped together into the following 4 levels (determined by numLevels):</p>
   * <table>
   * <th><td>Polyline Level</td><td>Zoom Levels</td></th>
   * <tr><td>Level 0</td><td>Zoom levels 0-5</td></tr>
   * <tr><td>Level 1</td><td>Zoom levels 6-10</td></tr>
   * <tr><td>Level 2</td><td>Zoom levels 11-14</td></tr>
   * <tr><td>Level 3</td><td>Zoom levels 15-18</td></tr>
   * </table>
   * <p>An polyline level value of 3 in this case would ensure that a point appears in all zoom level,
   * while a level of 0 will ignore points specified beyond zoom level 5. The endpoints of a polyline
   * should often be set to the largest encoded level, to ensure that the endpoints will appear in
   * all zoom levels and the line is drawn correctly.</p>
   * <p>A general rule is that whenever the level of the end point is greater or equal to the current Gmaps level,
   * that end point is appeared; otherwise, it should be skipped.</p>
   *
   * @param lat the latitude
   * @param lng the longitude
   * @param level the polyline level(default to 0 - 3)
   * @deprecated As of release 3.0.2, replaced with {@link #addPath(LatLng)} instead.
   */
  public void addPoint(double lat, double lng, int level) {
    if (lat > 90.0 || lat < -90.0) {
      throw new IllegalArgumentException("latitude must be from -90 ~ +90: " +lat);
    }
    if (lng > 180.0 || lng < -180.0) {
      throw new IllegalArgumentException("longtitude must be from -180 ~ +180: "+lng);
    }
    addPath(new LatLng(lat, lng));
  }
 
  /**
   * Add polyline axix point.
   * @param latLng the latitude and longitude of the point
   * @since 3.0.2
   */
  public void addPath(LatLng latLng) {
    _path.add(latLng);
    clearCacheAndSmartUpdatePth();
  }
 
  /*
  protected void smartUpdatePoly() {
    if (_smartUpdatePoly) { //already mark smart draw
      return;
    }
    _smartUpdatePoly = true;
    Events.postEvent("onSmartUpdatePoly", this, null);
  }
  private void clearCacheAndSmartUpdate() {
    _encodedPolyline = null; //cached value
    smartUpdatePoly();
  }
  */
 
  /**
   * Set polyline in lat,lng,level tuple(double, double, int);
   * e.g. "lat1,lng1,level1,lat2,lng2,level2,lat3,lng3,level3, ...".
   * @param points
   * @deprecated As of release 3.0.2, replaced with {@link #setPath(String)} instead.
   */
  public void setPoints(String points) {
    String[] arr = points.split(",");
    int length = arr.length;
    final StringBuffer sb = new StringBuffer();
    for(int i = 0; i < length; sb.append(","), i += 3) {
      sb.append(arr[i]).append(",").append(arr[i+1]);
    }
    setPath(sb.toString());
  }
 
  /**
   * Sets ordered sequence of coordinates of the Polyline.
   * e.g. "lat1,lng1,lat2,lng2,lat3,lng3, ...".
   * @param path the path of the Polyline.
   * @since 3.0.2
   */
  public void setPath(String path) {
    if (!Objects.equals(path, _strpath)) {
      _strpath = path;
      if (path != null) {
        _path.clear();
        final Collection list = CollectionsX.parse(null, path, ',');
        for(final Iterator it = list.iterator(); it.hasNext();) {
          double lat = Double.parseDouble((String) it.next());
          double lng = Double.parseDouble((String) it.next());
          _path.add(new LatLng(lat, lng));
        }
      }
      clearCacheAndSmartUpdatePth();
    }
  }

  /**
   * Sets polyline in list
   * @param path the List of LatLng
   * @since 3.0.2
   */
  public void setPath(List path) {
    if(!Objects.equals(_path, path)) {
      _path = path;
      clearCacheAndSmartUpdatePth();
    }
  }
 
  /**
   * Returns the polyline path in list
   * @return the List of LatLng
   * @since 3.0.2
   */
  public List getPath() {
    return _path;
  }

  private void clearCacheAndSmartUpdatePth() {
    _encodedPolyline = null;
    smartUpdate("path", getEncodedPolyline());
  }
  /*
  private String[] getPointsAndLevels() {
    return new String[] {getEncodedPolyline(), getEncodedLevels()};
  }
  */
  public String getEncodedPolyline() {
    if (_encodedPolyline == null) {
      int lat = 0;
      int lng = 0;
      final StringBuffer sb = new StringBuffer(_path.size()*4);
      for(final Iterator it = _path.iterator(); it.hasNext();) {
        LatLng latLng = (LatLng) it.next();
        final int tlat = e5(latLng.getLatitude());
        final int tlng = e5(latLng.getLongitude());
        sb.append(encodeLatLng(tlat - lat)).append(encodeLatLng(tlng - lng));
        lat = tlat;
        lng = tlng;
      }
      _encodedPolyline = sb.length() == 0 ? "??" : sb.toString();
    }
    return _encodedPolyline;
  }
 
  protected int e5(double db) {
    return (int) Math.floor(db * 100000);
  }
  /*
  public String getEncodedLevels() {
    if (_encodedLevels == null) {
      final StringBuffer sb = new StringBuffer(_levels.size()*2);
      for(final Iterator<Integer> it = _levels.iterator(); it.hasNext();) {
        sb.append(encodeInt(it.next()));
      }
      _encodedLevels = sb.length() == 0 ? "?" : sb.toString();
    }
    return _encodedLevels;
  }
  */
  protected static String encodeLatLng(int e5) {
    boolean sign = e5 < 0;
    e5 <<=1; //shift left
    if (sign) e5 ^= 0xffffffff;
    return encodeInt(e5);
  }
 
  protected static String encodeInt(int x) {
    final StringBuffer sb = new StringBuffer(6);
    do {
      int chunk = (x & 0x1f);
      x >>>= 5;
      if (x != 0) {
        chunk |= 0x20;
      }
      chunk += 63;
      sb.append((char)chunk);
    } while(x != 0);
    return sb.toString();
  }
 
  /**
   * Set encoded path to polyline and decode path to
   * e.g. "_p~iF~ps|U_ulLnnqC_mqNvxq`@".
   * @param encodedPolyline the encoded path of Polyline.
   * @since 3.0.2
   */
  public void setEncodedPolylineByClient(String encodedPolyline) {
    if (!Objects.equals(encodedPolyline, _encodedPolyline)) {
      _encodedPolyline = encodedPolyline;
      decodePolyline(_encodedPolyline);
    }
  }
 
  protected void decodePolyline(String encodedPolyline) {
    Object[] values = getDecodeLatLngs(encodedPolyline);
    int length = values.length;
    _path.clear();
    if (length > 2) {
      _path.add(new LatLng(((Double)values[0]).doubleValue(), ((Double)values[1]).doubleValue()));
      for (int i = 2; i < length - 1; i += 2) {
        final double preLat = ((LatLng) _path.get(i/2 - 1)).getLatitude();
        final double preLng = ((LatLng) _path.get(i/2 - 1)).getLongitude();
        _path.add(new LatLng( ((Double)values[i]).doubleValue() + preLat, ((Double)values[i+1]).doubleValue() + preLng));
      }
    }
  }
 
  protected static Object[] getDecodeLatLngs(String encodedPolyline) {
    int length = encodedPolyline.length();
    int index = 0, value = 0, shift = 0;
    List decodeLatLngs = new LinkedList();
    while (index < length) {
      int chunk = ((int) encodedPolyline.charAt(index ++)) - 63;
      value |= (chunk & 0x1f) << shift;
      shift += 5;
      if ((chunk & 0x20) == 0) {
        decodeLatLngs.add(decodeInt(value));
        value = 0;
        shift = 0;
      }
    }
    return decodeLatLngs.toArray();
  }
 
  protected static Double decodeInt(int x) {
    boolean sign = ((x & 1) != 0);
    double value = sign ? ~(x >> 1) : (x >> 1);
    return new Double(value / 1e5);
  }
 
  /**
   * Returns the line color in form of #RRGGBB, default to #808080.
   * @return the color
   */
  public String getColor() {
    return _color;
  }

  /**
   * Sets the line color in form of #RRGGBB, default to #808080.
   * @param color the color to set
   */
  public void setColor(String color) {
    if (color == null) color = "#808080";
    if (!color.equals(_color)) {
      this._color = color;
      smartRerender();
    }
  }

  /**
   * Returns the line weight(width) 1 - 10, default to 5.
   * @return the line weight
   */
  public int getWeight() {
    return _weight;
  }

  /**
   * Sets the line weight(width) 1 - 10, default to 5.
   * @param weight the line weight
   */
  public void setWeight(int weight) {
    if (weight != _weight) {
      this._weight = weight;
      smartRerender();
    }
  }
 
  /**
   * Returns the line opacity 0 - 100, default to 50.
   * @return the opacity
   * @since 2.0_7
   */
  public int getOpacity() {
    return _opacity;
  }

  /**
   * Sets the line opacity 0 - 100, default to 50.
   * @param opacity the line opacity to set
   * @since 2.0_7
   */
  public void setOpacity(int opacity) {
    if (opacity < 0 || opacity > 100) {
      throw new UiException("Line opacity must be between 0 to 100 (inclusive): "+opacity);
    }
    if (opacity != _opacity) {
      this._opacity = opacity;
      smartRerender();
    }
  }

  /**
   * Returns the number of polyline levels.
   * @return the number of polyline levels.
   * @deprecated As of release 3.0.2, Google v3 not supported.
   */
  public int getNumLevels() {
    return _numLevels;
  }
 
  /**
   * Sets the number of polyline levels.
   * @param numLevels the number of levels.
   * @deprecated As of release 3.0.2, Google v3 not supported.
   */
  public void setNumLevels(int numLevels) {
    if (numLevels < 1 || numLevels > 19) {
      throw new IllegalArgumentException("numLevels must be from 1 ~ 19: " + numLevels);
    }
    if (_numLevels != numLevels) {
      this._numLevels = numLevels;
     
      //calculate proper zoomFactor
      int factor = 19/numLevels;
      int mod = 19%numLevels;
     
      //if mod is larger than numLevels / 2
      _zoomFactor = 2 << ((mod > (numLevels >> 1)) ? (factor + 1) : factor);
     
      smartRerender();
    }
  }
 
  /** Returns whether can edit the Gpolyline by mouse; default to false.
   * @return whether can edit the Gpolyline by mouse; default to false.
   * @since 3.0.2
   */
  public boolean isEditable () {
    return _editable;
  }
 
  /** Sets whether can edit the Gpolyline by mouse; default to false.
   * @param editable whether enable edit the Gpolyline by mouse.
   * @since 3.0.2
   */
  public void setEditable (boolean editable) {
    if (_editable != editable) {
      _editable = editable;
      smartUpdate("editable", _editable);
    }
  }
 
  /**
   * Returns the zoomFactor (zoomFactor change per the numLevels).
   * @deprecated As of release 3.0.2, Google v3 not supported.
   */
  public int getZoomFactor() {
    return _zoomFactor;
  }
 
  protected void prepareRerender(Map info) {
    //info.put("pointsAndLevels", getPointsAndLevels());
    info.put("path", getEncodedPolyline());
    info.put("color", getColor());
    info.put("weight", new Integer(getWeight()));
    info.put("opacity", new Double(getOpacity()/100.0));
    info.put("editable", new Boolean(isEditable()));
  }

  protected void smartRerender() {
    final Map info = new HashMap();
    prepareRerender(info);
    smartUpdate("rerender_", info);
  }
 
  //-- Component --//
  /** Default: not childable.
   */
  public boolean isChildable() {
    return false;
  }
 
  protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
  throws java.io.IOException {
    super.renderProperties(renderer);
    //render(renderer, "pointsAndLevels", getPointsAndLevels());
    render(renderer, "path", getEncodedPolyline());
    render(renderer, "color", getColor());
    render(renderer, "weight", new Integer(getWeight()));
    render(renderer, "opacity", new Double(getOpacity()/100.0));
    render(renderer, "editable", isEditable());
  }
  /** Processes an AU request.
   *
   * <p>Default: in addition to what are handled by {@link XulElement#service},
   * it also handles onClick.
   * @since 3.0.2
   */
  public void service(AuRequest request, boolean everError) {
    final String cmd = request.getCommand();
    if ("onPathChange".equals(cmd)) {
      final PathChangeEvent evt = PathChangeEvent.getPathChangeEvent(request);
      setEncodedPolylineByClient(evt.getPath());
      Events.postEvent(evt);
    } else
      super.service(request, everError);
  }
}
TOP

Related Classes of org.zkoss.gmaps.Gpolyline

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.