Package org.timepedia.chronoscope.client.overlays

Source Code of org.timepedia.chronoscope.client.overlays.Marker

package org.timepedia.chronoscope.client.overlays;

import org.timepedia.chronoscope.client.Cursor;
import org.timepedia.chronoscope.client.InfoWindow;
import org.timepedia.chronoscope.client.InfoWindowClosedHandler;
import org.timepedia.chronoscope.client.InfoWindowEvent;
import org.timepedia.chronoscope.client.XYPlot;
import org.timepedia.chronoscope.client.canvas.Color;
import org.timepedia.chronoscope.client.canvas.Layer;
import org.timepedia.chronoscope.client.canvas.View;
import org.timepedia.chronoscope.client.event.ChartDragEvent;
import org.timepedia.chronoscope.client.gss.GssElement;
import org.timepedia.chronoscope.client.gss.GssProperties;
import org.timepedia.chronoscope.client.plot.DefaultXYPlot;
import org.timepedia.chronoscope.client.render.GssElementImpl;
import org.timepedia.chronoscope.client.util.DateFormatter;
import org.timepedia.chronoscope.client.util.MathUtil;
import org.timepedia.chronoscope.client.util.date.DateFormatterFactory;
import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.ExportPackage;
import org.timepedia.exporter.client.Exportable;

import java.util.ArrayList;
import java.util.Date;

@ExportPackage("chronoscope")
public class Marker extends DraggableOverlay implements GssElement, Exportable {

  private InfoWindow currentWindow;

  private InfoWindow wasOpenWindow;

  private String typeClass;

  protected GssProperties guideLineProps;

  protected DateFormatter guideLineDateFmt;

  private static enum MarkerShape {
    BALLOON, TEARDROP;
  }

  // Determines how high (stretched-out) the marker is.
  private static final int MARKER_HEIGHT = 15;

  private ArrayList<OverlayClickListener> clickListeners;

  private int datasetIdx = -1;

  protected double domainX;

  private boolean isScreenPropsSet = false;

  private String label;

  protected GssProperties markerProps;

  private MarkerShape markerShape;

  protected double rangeY;

  protected int labelWidth, labelHeight;

  protected boolean guideLine = true;

  protected String guideLineDate = null;

  @Export
  public boolean isGuideLine() {
    return guideLine;
  }

  @Export
  public void setGuideLine(boolean guideLine) {
    this.guideLine = guideLine;
  }

  public String getGuideLineDate() {
    return guideLineDate;
  }

  @Export
  public void setGuideLineDateFormat(String guideLineDate) {
    this.guideLineDate = guideLineDate;
    if (guideLineDate != null) {
      this.guideLineDateFmt = DateFormatterFactory.getInstance()
          .getDateFormatter(guideLineDate);
    }
  }

  public Marker(double domainX, String label, int datasetIdx) {
    this.domainX = domainX;
    this.label = label;
    typeClass = label;
    // Silently fix an invalid dataset index
    this.datasetIdx = Math.max(0, datasetIdx);
  }

  public Marker(double domainX, String label, int datasetIdx,
      String typeClass) {
    this.domainX = domainX;
    this.label = label;
    this.typeClass = typeClass;
    // Silently fix an invalid dataset index
    this.datasetIdx = Math.max(0, datasetIdx);
  }

  /**
   */
  @Export
  public Marker(String date, int datasetIdx, String label) {
    this(Date.parse(date), label, datasetIdx);
  }

  @Export
  public Marker(String date, int datasetIdx, String label, String typeClass) {
    this(Date.parse(date), label, datasetIdx, typeClass);
  }

  /**
   */
  @Export("addOverlayListener")
  public void addOverlayClickListener(OverlayClickListener ocl) {
    if (clickListeners == null) {
      clickListeners = new ArrayList<OverlayClickListener>();
    }
    clickListeners.add(ocl);
  }

  public void click(int x, int y) {
    if (clickListeners != null) {
      for (OverlayClickListener l : clickListeners) {
        l.onOverlayClick(this, x, y);
      }
    }
  }

  public void draw(Layer backingCanvas, String layer) {
    if (plot == null) {
      throw new IllegalStateException("plot not set");
    }
    if (handleInfoWindowVisibility()) {
      return;
    }

    lazyInitScreenProps(backingCanvas);

    double x = plot.domainToScreenX(domainX, datasetIdx);
    double yp = plot.rangeToScreenY(rangeY, datasetIdx);
    double y = yp;

    double proposedMarkerTop = y - MARKER_HEIGHT - labelHeight;
    if (proposedMarkerTop > plot.getInnerBounds().y) {
      y = proposedMarkerTop;
      markerShape = MarkerShape.BALLOON;
    } else {
      y += MARKER_HEIGHT;
      markerShape = MarkerShape.TEARDROP;
    }

    backingCanvas.save();
    if ((guideLine || guideLineProps.visible) && !isDragging()) {
      drawGuideLine(backingCanvas, (int) x);
    }

    int arcDirection = (y < yp) ? 1 : 0;
    if (currentWindow != null) {
      currentWindow.setPosition(plot.domainToWindowX(domainX, datasetIdx),
          plot.rangeToWindowY(rangeY, datasetIdx) + 5);
    }
    x = drawOval(labelWidth + 1, labelHeight - 3, markerProps, backingCanvas,
        x, y, yp, arcDirection);

    backingCanvas
        .drawText(x, y + 12, label, markerProps.fontFamily, markerProps.fontWeight,
            markerProps.fontSize, layer, Cursor.CONTRASTED);
    backingCanvas.restore();
  }

  protected boolean handleInfoWindowVisibility() {
    // if no longer visible, close window
    if (!plot.getDomain().containsOpen(domainX)) {
      if (currentWindow != null) {
        wasOpenWindow = currentWindow;
        currentWindow.close();
      }
      return true;
    }

    // if window was hidden, and marker is now visible, show it
    if (currentWindow == null && wasOpenWindow != null) {
      currentWindow = wasOpenWindow;
      wasOpenWindow = null;
      currentWindow.open();
    }
    return false;
  }

  public int getDatasetIndex() {
    return this.datasetIdx;
  }

  @Export
  public double getDomainX() {
    return domainX;
  }

  public GssElement getParentGssElement() {
    return null;
  }

  @Export
  public double getRangeY() {
    return rangeY;
  }

  public String getType() {
    return "marker";
  }

  public String getLabel() {
    return label;
  }

  public void setLabel(String label) {
    this.label = label;
  }

  public String getTypeClass() {
    return typeClass;
  }
  // NOTE - DatasetRenderer.drawFocusPointGuideLine is for points with guidelines (eg point:focus guideline) this is for marker guidelines
  // TODO - DRY
   public void drawGuideLine(Layer layer, int x) {
    if(guideLineProps == null) return;
   
    layer.save();
    layer.setFillColor(guideLineProps.color);
    double lt = Math.max(guideLineProps.lineThickness, 1);
    int coffset = (int) Math.floor(lt / 2.0);

    layer.fillRect(x - coffset, 0, lt, layer.getBounds().height);
    if (guideLineDate != null) {
      layer.setStrokeColor(Color.BLACK);
      int hx = x;
      double dx = ((DefaultXYPlot) plot)
          .windowXtoDomain(hx + ((DefaultXYPlot) plot).getBounds().x);
      String label = guideLineDateFmt.format(dx);
      hx += dx < plot.getDomain().midpoint() ? 1.0
          : -1 - layer.stringWidth(label, "Helvetica", "", "8pt");
      // TODO - factor hard-coded font out
      layer.drawText(hx, plot.getInnerBounds().y + 10, label, "Helvetica", "", "8pt", "overlays", Cursor.CLICKABLE);
    }
    layer.restore();
  }

  public boolean isHit(int x, int y) {
    if (plot == null) {
      throw new IllegalStateException("plot not set");
    }

    final double mx = plot.domainToWindowX(domainX, datasetIdx);
    final double xPad = labelWidth / 2 + 3;
    final boolean isHitX = MathUtil.isBounded(x, mx - xPad, mx + xPad);

    final double my = plot.rangeToWindowY(rangeY, datasetIdx);
    boolean isHitY;
    if (markerShape == MarkerShape.BALLOON) {
      double topOfBalloon = my - MARKER_HEIGHT - labelHeight;
      isHitY = MathUtil.isBounded(y, topOfBalloon, my);
    } else { // assumed to be TEARDROP
      double bottomOfTeardrop = my + MARKER_HEIGHT + labelHeight;
      isHitY = MathUtil.isBounded(y, my, bottomOfTeardrop);
    }

    return isHitX && isHitY;
  }

  /**
   */
  @Export
  public InfoWindow openInfoWindow(String html) {
    if (plot == null) {
      throw new IllegalStateException("plot not set");
    }
    if (currentWindow != null) {
      currentWindow.close();
    }
    wasOpenWindow = null;
    currentWindow = plot.openInfoWindow(html, domainX, rangeY, datasetIdx);
    currentWindow.addInfoWindowClosedHandler(new InfoWindowClosedHandler() {
      public void onInfoWindowClosed(InfoWindowEvent event) {
        currentWindow = null;
      }
    });
    return currentWindow;
  }

  public void removeOverlayClickListener(OverlayClickListener listener) {
    if (clickListeners != null) {
      clickListeners.remove(listener);
    }
  }

  public void setDatasetIndex(int datasetIndex) {
    this.datasetIdx = datasetIndex;
  }

  public void setPlot(XYPlot plot) {
    super.setPlot(plot);
    if (plot != null) {
      rangeY = interpolateRangeY(domainX, datasetIdx);
    }
  }

  public String toString() {
    return this.label;
  }

  public static double drawOval(int width, int height,
      GssProperties markerProperties, Layer backingCanvas, double x, double y,
      double yp, int dir) {
    backingCanvas.setStrokeColor(markerProperties.color);
    backingCanvas.setTransparency(1.0f);
    backingCanvas.beginPath();

    x -= width / 2;
    backingCanvas.setShadowOffsetX(0);
    backingCanvas.setShadowOffsetY(0);
    backingCanvas.setShadowBlur(0);

    /*
    double startAngle = Math.PI * 2.0 - Math.PI / 2 + Math.PI / 8;
    double endAngle = Math.PI * 2.0 - Math.PI / 2 - Math.PI / 8;
    if (dir == 0) {
      startAngle = Math.PI * 2.0 - Math.PI / 2 - Math.PI / 4 - Math.PI / 8;
      endAngle = Math.PI * 2.0 - Math.PI / 2 + Math.PI / 4 + Math.PI / 8;
    }
    */

    backingCanvas
        .arc(x + width / 2, y + height / 2, width + 1, 0, Math.PI, dir);
    backingCanvas.lineTo(x + (width + 1) / 2, yp);
    backingCanvas.closePath();

    backingCanvas.setFillColor(markerProperties.bgColor);
    backingCanvas.fill();
    backingCanvas.setLineWidth(markerProperties.lineThickness);
    backingCanvas.setShadowOffsetX(3);
    backingCanvas.setShadowOffsetY(3);
    backingCanvas.setShadowBlur(3);
    backingCanvas.stroke();
    return x;
  }

  private int drawBox(Layer backingCanvas, int x, int y) {
    backingCanvas.setStrokeColor(Color.BLACK);
    backingCanvas.setTransparency(1.0f);
    backingCanvas.beginPath();

    x -= labelWidth / 2;
    backingCanvas.setShadowOffsetX(0);
    backingCanvas.setShadowOffsetY(0);
    backingCanvas.setShadowBlur(0);
    backingCanvas.moveTo(x - 1, y);
    backingCanvas.lineTo(x + labelWidth + 3, y);
    backingCanvas.lineTo(x + labelWidth + 3, y + labelHeight);
    backingCanvas.lineTo(x - 1, y + labelHeight);
    backingCanvas.closePath();
    backingCanvas.setFillColor(new Color(200, 200, 200));
    backingCanvas.fill();
    backingCanvas.setLineWidth(1);
    backingCanvas.stroke();
    return x;
  }

  protected double interpolateRangeY(double domainX, int datasetIdx) {
    int p = plot.getNearestVisiblePoint(domainX, datasetIdx) - 1;
    p = Math.max(p, 0);

    // linearly interpolate rangeY from domainX and its surrounding 2 data points
    double d0 = plot.getDataX(datasetIdx, p);
    double d1 = plot.getDataX(datasetIdx, p + 1);
    double r0 = plot.getDataY(datasetIdx, p);
    double r1 = plot.getDataY(datasetIdx, p + 1);

    double interplatedRangeY = r0 + (domainX - d0) / (d1 - d0) * (r1 - r0);
    return interplatedRangeY;
  }

  protected void lazyInitScreenProps(Layer layer) {
    if (!isScreenPropsSet) {
      View view = plot.getChart().getView();
      markerProps = view.getGssProperties(this, "");
      labelWidth = layer.stringWidth(label, markerProps.fontFamily, "normal",
          markerProps.fontSize);
      labelHeight = layer.stringHeight(label, markerProps.fontFamily, "normal",
          markerProps.fontSize) + 2;
      guideLineProps = view
          .getGssProperties(new GssElementImpl("guideline", this), "");

      setGuideLine(guideLineProps.visible);
      setGuideLineDateFormat(guideLineProps.dateFormat);
      isScreenPropsSet = true;
    }
  }

  @Override
  public void onDrag(ChartDragEvent event) {

    int point = plot.getNearestVisiblePoint(
        ((DefaultXYPlot) plot).windowXtoDomain(event.getCurrentX()),
        datasetIdx);
//    rangeY = interpolateRangeY(domainX, getDatasetIndex());
    if (point > -1) {
      domainX = plot.getDataX(datasetIdx, point);
      rangeY = plot.getDataY(datasetIdx, point);
    }
    super.onDrag(event);
  }
}
TOP

Related Classes of org.timepedia.chronoscope.client.overlays.Marker

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.