package tripvisUI.visualisations.circleVis { import com.google.maps.LatLng; import com.google.maps.LatLngBounds; import com.google.maps.PaneId; import com.google.maps.interfaces.IMap; import com.google.maps.interfaces.IPane; import com.google.maps.overlays.Marker; import com.google.maps.overlays.MarkerOptions; import com.google.maps.overlays.OverlayBase; import flash.geom.Point; import mx.collections.ArrayCollection; import mx.controls.Label; import mx.formatters.DateFormatter; import tripvisModel.TripItem; public class CircleOverlay extends OverlayBase { private const CIRCLE_THICKNESS: Number = 80; private const CIRCLE_OFFSET: Number = 80; private var _markers: ArrayCollection = new ArrayCollection(); private var _tripItems: ArrayCollection; private var _circleType: CircleType; public function CircleOverlay() { super(); _tripItems = new ArrayCollection(); circleType = CircleType.DISTANCE; //default waarde } public function set tripItems(value: ArrayCollection): void { _tripItems = value; } public function set circleType(value: CircleType): void { _circleType = value; } override public function positionOverlay(zoomChanged: Boolean): void { update(); } public function clear(): void { graphics.clear(); // alle markers (labels) van de vorige keer wegsmijten: for each (var marker: Marker in _markers) { pane.map.removeOverlay(marker); } } public function update(): void { this.clear(); if ( _tripItems.length != 0) { var latlngbounds: LatLngBounds = getBoundingBox(_tripItems); var centerPoint: Point = pane.fromLatLngToPaneCoords( latlngbounds.getCenter() ); var cornerPoint: Point = pane.fromLatLngToPaneCoords( latlngbounds.getSouthEast() ); var distanceVector: Point = cornerPoint.subtract(centerPoint); var distance: Number = distanceVector.length; var radius: Number = distance + CIRCLE_OFFSET + CIRCLE_THICKNESS/2; drawMainCircle(centerPoint, radius, CIRCLE_THICKNESS); //drawLine(centerPoint, cornerPoint, 3); // punten tekenen: var tripItemPoints: ArrayCollection = new ArrayCollection(); for each(var tripItem: TripItem in _tripItems) { tripItemPoints.addItem( pane.fromLatLngToPaneCoords( tripItem.location.getLatLng() ) ); } // start angle berekenen, hangt af van eerste tripItemPunt var startAngle: Number = calculateAngleInCircle(centerPoint, tripItemPoints[0]); var angles: ArrayCollection; switch (this._circleType) { case CircleType.DISTANCE: angles = calculateAllAngles( calculateAllDistances(tripItemPoints), startAngle, _circleType ); break; case CircleType.DURATION: angles = calculateAllAngles( calculateAllDurations(_tripItems), startAngle, _circleType ); break; } var pointsOnInnerCircle: ArrayCollection = calculateCirclePoints(angles, centerPoint, radius - CIRCLE_THICKNESS/2); var pointsOnOuterCircle: ArrayCollection = calculateCirclePoints(angles, centerPoint, radius + CIRCLE_THICKNESS/2); // let the drawing begin: for (var i: uint = 0; i < _tripItems.length; i++) { drawDot(tripItemPoints[i]); var curvePoint: Point = calculateCurvePoint(tripItemPoints[i], pointsOnInnerCircle[i]); drawCurve(tripItemPoints[i], pointsOnInnerCircle[i], curvePoint); if (i == 0) drawLine(pointsOnInnerCircle[i], pointsOnOuterCircle[i], 3); else drawLine(pointsOnInnerCircle[i], pointsOnOuterCircle[i], 1); // datums plaatsen: var dateLabel: Label = new Label(); var dateLocation: LatLng = pane.fromPaneCoordsToLatLng( pointsOnInnerCircle[i] ); var dateFormatter: DateFormatter = new DateFormatter(); dateFormatter.formatString = "J:NN"; dateLabel.text = dateFormatter.format(_tripItems[i].timePeriod.startDate) + ' - ' + dateFormatter.format(_tripItems[i].timePeriod.endDate); var angleDegrees: Number = angles[i] * 180/Math.PI; if ( (angleDegrees >= 110 && angleDegrees <= 250) || (angleDegrees >= -180 && angleDegrees <= -110) ) { angleDegrees = 180 + angleDegrees; dateLocation = pane.fromPaneCoordsToLatLng( pointsOnOuterCircle[i] ); } dateLabel.rotation = angleDegrees; dateLabel.setStyle('fontFamily', 'EmbeddedArial'); dateLabel.setStyle('fontSize', '12'); var markerOptions: MarkerOptions = new MarkerOptions(); markerOptions.icon = dateLabel; var labelMarker: Marker = new Marker(dateLocation, markerOptions); pane.map.addOverlay(labelMarker); this._markers.addItem(labelMarker); // einde datums plaatsen } }// _tripItems != 0 } private function getBoundingBox(tripItems: ArrayCollection): LatLngBounds { var firstTripItem: TripItem = tripItems[0]; var firstLatLng: LatLng = firstTripItem.location.getLatLng(); var latlngbounds: LatLngBounds = new LatLngBounds(firstLatLng, firstLatLng); for (var i: uint = 1; i < tripItems.length; i++) { var tripItem: TripItem = tripItems[i]; latlngbounds.extend(tripItem.location.getLatLng()); } return latlngbounds; } private function calculateAllDistances(tripItemPoins: ArrayCollection): ArrayCollection { /* vb: tripItems[0] = 0; tripItems[1] = 50 cm; ... tripItems[n] = total cm; */ var totalDistance: Number = 0; var allDistances: ArrayCollection = new ArrayCollection(); allDistances.addItem(0); //afstand tot eerte element is 0 for (var i: uint = 1; i < tripItemPoins.length; i++) { var a: Point = tripItemPoins[i-1]; var b: Point = tripItemPoins[i]; var distanceVector: Point = b.subtract(a); var distance: Number = distanceVector.length; totalDistance += distance; allDistances.addItem(totalDistance); } return allDistances; } private function calculateAllDurations(tripItems: ArrayCollection): ArrayCollection { var totalDuration: Number = 0; // dus van iedere tripItem (die al in een bepaalde volgorde staat, hebben we een tijdsperdiode nodig: var allDurations: ArrayCollection = new ArrayCollection(); // dus in 0 moet 0 komen // en in 1 moet de tijd van 0 komen allDurations.addItem(0); for (var i: uint = 1; i < tripItems.length + 1; i++) { var previousTripItem: TripItem = tripItems[i-1]; var duration: Number = previousTripItem.timePeriod.getDuration(); totalDuration += duration; allDurations.addItem(totalDuration); } return allDurations; } private function calculateAllAngles(distances: ArrayCollection, startAngle: Number, type: CircleType): ArrayCollection { var angles: ArrayCollection = new ArrayCollection(); var angle: Number; if (type == CircleType.DISTANCE) { // laatste element bevat totalDistance: var totalDistance: Number = distances[distances.length-1]; for each(var distance: Number in distances) { angle = distance * (2*Math.PI - 0.05) / totalDistance + startAngle; angles.addItem(angle); } return angles; } else { var totalDuration: Number = distances[distances.length-1]; for (var i: uint; i < distances.length-1; i++) { var duration: Number = distances[i]; angle = duration * 2*Math.PI / totalDuration + startAngle; angles.addItem(angle); } return angles; } } private function calculateCirclePoints(angles: ArrayCollection, centerPoint: Point, radius: Number): ArrayCollection { var circlePoints: ArrayCollection = new ArrayCollection(); for each(var angle: Number in angles) { var circlePoint: Point = calculatePointOnCircle(centerPoint, angle, radius); circlePoints.addItem(circlePoint); } return circlePoints; } private function calculateCirclePoint(point: Point, allPoints: ArrayCollection, center: Point, radius: Number): Point { if (allPoints.length == 1) { var d: Point = point.subtract(center); var angle1: Number = Math.atan2(d.y, d.x); var circlePointX: Number = center.x + radius * Math.cos(angle1) ; var circlePointY: Number = center.y + radius * Math.sin(angle1); return new Point(circlePointX, circlePointY); } else { var totalDistance: Number = 0; var distanceToPoint: Number = 0; var countDistanceToPoint: Boolean = true; for (var i: uint = 0; i < allPoints.length - 1; i++) { var firstRealPoint: Point = allPoints[i]; var secondRealPoint: Point = allPoints[i+1]; var distance: Number = ( secondRealPoint.subtract(firstRealPoint) ).length; totalDistance += distance; if (countDistanceToPoint) distanceToPoint += distance; if (allPoints[i+1] == point) countDistanceToPoint = false; } var startAngle: Number = calculateAngleInCircle(center, allPoints[0]); var angle: Number = startAngle + 2*Math.PI*distanceToPoint/totalDistance; var pointOnInnerCirle: Point = calculatePointOnCircle(center, angle, radius); return pointOnInnerCirle; } return null; } private function calculateCurvePoint(pointA: Point, pointB: Point): Point { var b_minus_a: Point = pointB.subtract(pointA); var middleDistance: Number = b_minus_a.length; var angle: Number = Math.atan2(b_minus_a.y, b_minus_a.x); var middlePointRelativeToCenter: Point = new Point(middleDistance/2*Math.cos(angle), middleDistance/2*Math.sin(angle)); var middlePoint: Point = pointA.add(middlePointRelativeToCenter); var distanceFromMiddlePoint: Number = 40; var curvePointRelativeToCenter: Point = new Point( - distanceFromMiddlePoint*Math.cos(Math.PI/2-angle) , distanceFromMiddlePoint*Math.sin(Math.PI/2-angle) ); var curvePoint: Point = middlePoint.add(curvePointRelativeToCenter); return curvePoint; } private function calculateAngleInCircle(center: Point, other: Point): Number { var d: Point = other.subtract(center); return Math.atan2(d.y, d.x); } private function calculatePointOnCircle(center: Point, angle: Number, distanceFromCenter: Number): Point { var circlePointX: Number = center.x + distanceFromCenter * Math.cos(angle) ; var circlePointY: Number = center.y + distanceFromCenter * Math.sin(angle); return new Point(circlePointX, circlePointY); } private function drawDot(point: Point): void { graphics.lineStyle(3, 0x855825); graphics.beginFill(0x855825); graphics.drawCircle(point.x, point.y, 4); graphics.endFill(); } private function drawLine(start: Point, end: Point, thickness: Number): void { graphics.lineStyle(thickness, 0x855825); graphics.moveTo(start.x, start.y); graphics.lineTo(end.x, end.y); } private function drawCurve(start: Point, end: Point, control: Point): void { graphics.lineStyle(3, 0x855825); graphics.moveTo(start.x, start.y); graphics.curveTo(control.x, control.y, end.x, end.y); } private function drawMainCircle(centerPoint: Point, radius: Number, thickness: Number): void { graphics.lineStyle(thickness, 0xCCA170); graphics.drawCircle(centerPoint.x, centerPoint.y, radius); /* // Assen: graphics.lineStyle(3, 0xFF0000); graphics.moveTo(centerPoint.x, centerPoint.y); graphics.lineTo(centerPoint.x + 400, centerPoint.y); graphics.lineStyle(3, 0x0000FF); graphics.moveTo(centerPoint.x, centerPoint.y); graphics.lineTo(centerPoint.x, centerPoint.y + 400); */ } override public function getDefaultPane(map: IMap):IPane { return map.getPaneManager().getPaneById(PaneId.PANE_OVERLAYS); } } }