I am trying to get middle point of a line (a line in a rectangle polygon), but following code does not give me correct answer. The number is too small. Can anyone help?
The code is modified from here: http://www.yourhomenow.com/house/haversine.html
var p1 = myRectPolygon.getPath().getAt(0);
var p2 = myRectPolygon.getPath().getAt(3);
var lat1 = p1.lat().toRad();
var lat2 = p2.lat().toRad();
var lon1 = p1.lng().toRad();
var dLon = (p2.lng() - p1.lng()).toRad();
var Bx = Math.cos(lat2) * Math.cos(dLon);
var By = Math.cos(lat2) * Math.sin(dLon);
lat3 = Math.atan2(Math.sin(lat1)+Math.sin(lat2),
Math.sqrt((Math.cos(lat1)+Bx)*(Math.cos(lat1)+Bx) + By*By ) );
lon3 = lon1.toRad() + Math.atan2(By, Math.cos(lat1) + Bx);
var mid_latLng = new google.maps.LatLng(lat3, lon3);
You are converting lon1 to rad twice:
You declared:
var lon1 = p1.lng().toRad();
And later you do:
lon3 = lon1.toRad() + Math.atan2(By, Math.cos(lat1) + Bx);
try changing it to
lon3 = lon1 + Math.atan2(By, Math.cos(lat1) + Bx);
I also found this solution (Using the Haversine Formula in Javascript) which indicates that doing something like the following may not work correctly
(lon2-lon1).toRad();
and to instead do something like
var dLonDeg = lon2-lon1
var dlon = dLonDeg.toRad()
You can use the geometry library to compute the midpoint:
var p1 = myRectPolygon.getPath().getAt(0);
var p2 = myRectPolygon.getPath().getAt(3);
var mid_latLng = google.maps.geometry.spherical.computeOffset(p1,
google.maps.geometry.spherical.computeDistanceBetween(p1, p2) / 2,
google.maps.geometry.spherical.computeHeading(p1, p2));
proof of concept fiddle
Related
I have Image component inside some container with clipAndEnableScrolling property set to true. I need a static method which gets this Image, rotation angle and rotates Image around center point of container without loosing any previous transformations. The best method I've created adds error after few rotations.
I thing it must work like this
public static function rotateImageAroundCenterOfViewPort(image:Image, value:int):void
{
// Calculate rotation and shifts
var bounds:Rectangle = image.getBounds(image.parent);
var angle:Number = value - image.rotation;
var radians:Number = angle * (Math.PI / 180.0);
var shiftByX:Number = image.parent.width / 2 - bounds.x;
var shiftByY:Number = image.parent.height / 2 - bounds.y;
// Perform rotation
var matrix:Matrix = new Matrix();
matrix.translate(-shiftByX, -shiftByY);
matrix.rotate(radians);
matrix.translate(+shiftByX, +shiftByY);
matrix.concat(image.transform.matrix);
image.transform.matrix = matrix;
}
but it doesn't. Looks like I can't understand how transformation works(
If you are trying to rotate the object around it's center, I think you'll want some more like this:
var matrix:Matrix = image.transform.matrix;
var rect:Rectangle = image.getBounds( insertParentObject );
//translate matrix to center
matrix.translate(- ( rect.left + ( rect.width/2 ) ), - ( rect.top + ( rect.height/2 ) ) );
matrix.rotate(radians);
//translate back
matrix.translate(rect.left + ( rect.width/2 ), rect.top + ( rect.height/2 ) );
image.transform.matrix = matrix;
Also here is a link to the same SO question with varying answers including the one I provided:
Flex/ActionScript - rotate Sprite around its center
As discussed in the comments if you are looking to rotate an object around a point (that is the center of your container), here's a function that I think would work:
//pass rotateAmount as the angle you want to rotate in degrees
private function rotateAround( rotateAmount:Number, obj:DisplayObject, origin:Point, distance:Number = 100 ):void {
var radians:Number = rotateAmount * Math.PI / 180;
obj.x = origin.x + distance * Math.cos( radians );
obj.y = origin.y + distance * Math.sin( radians );
}
Then you just call it:
rotateAround( rotateAmount, image, new Point( container.width/2, container.height/2 ) );
The last parameter distance you can pass whatever you like, so for example if I wanted a distance of the image vector length:
var dx:Number = spr.x - stage.stageWidth/2;
var dy:Number = spr.y - stage.stageHeight/2;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
rotateAround( rotateAmount, image, new Point( container.width/2, container.height/2 ), dist );
Here's the solution I've found:
public static function rotateImageAroundCenterOfViewPort(image:Image, value:int):void
{
// Calculate rotation and shifts
var center:Point = new Point(image.parent.width / 2, image.parent.height / 2);
center = image.parent.localToGlobal(center);
center = image.globalToLocal(center);
var angle:Number = value - image.rotation;
var radians:Number = angle * (Math.PI / 180.0);
var shiftByX:Number = center.x;
var shiftByY:Number = center.y;
// Perform rotation
var matrix:Matrix = new Matrix();
matrix.translate(-shiftByX, -shiftByY);
matrix.rotate(radians);
matrix.translate(+shiftByX, +shiftByY);
matrix.concat(image.transform.matrix);
image.transform.matrix = matrix;
image.rotation = Math.round(image.rotation);
}
Teste only with the angles like 90, 180 etc. (I don't need any else).
I need to convert this function to v3 API. This function is used to create an ellipse under a marker when selected. I have tried with projection and overlay, but without success.
function makePolyPoints(_marker)
{
var polyPoints = Array();
var markerPoint= _marker.getLatLng();
var projection = G_NORMAL_MAP.getProjection();
var mapZoom = map.getZoom();
var clickedPixel = projection.fromLatLngToPixel(markerPoint, mapZoom);
var ellipseRadA = 20;
var ellipseRadB = 10;
var polyNumSides = 20;
var polySideLength = 18;
for (var a = 0; a<(polyNumSides+1); a++) {
var aRad = polySideLength*a*(Math.PI/180);
var pixelX = clickedPixel.x + ellipseRadA * Math.cos(aRad);
var pixelY = -3 + clickedPixel.y + ellipseRadB * Math.sin(aRad);
var polyPixel = new GPoint(pixelX,pixelY);
var polyPoint = projection.fromPixelToLatLng(polyPixel,mapZoom);
polyPoints.push(polyPoint);
}
return polyPoints;
}
Here the function for v3 that doesn't work, i think a problem of zoom level but I can't find how to replace projection.fromLatLngToPixel(markerPoint, mapZoom);
function makePolyPoints(_marker)
{
var polyPoints = Array();
var markerPoint= _marker.getPosition();
var projection = map.getProjection();
var mapZoom = map.getZoom();
var clickedPixel = projection.fromLatLngToPoint(markerPoint);
var ellipseRadA = 20;
var ellipseRadB = 10;
var polyNumSides = 20;
var polySideLength = 18;
for (var a = 0; a<(polyNumSides+1); a++) {
var aRad = polySideLength*a*(Math.PI/180);
var pixelX = clickedPixel.x + ellipseRadA * Math.cos(aRad);
var pixelY = -3 + clickedPixel.y + ellipseRadB * Math.sin(aRad);
var polyPixel = new google.maps.Point(pixelX,pixelY);
var polyPoint = projection.fromPointToLatLng(polyPixel);
polyPoints.push(polyPoint);
}
return polyPoints;
}
Hmm.. Im going to use one of my favorite gmap examples to help you out.
Using var clickedPixel = projection.fromLatLngToPoint(markerPoint); dont give you yet clickedPixel coordinates, they are still world coordinates. See projection specs.
Specifying own mercator projection when calculating that kind of stuff is useful. Look closely to following lines: (in this code example)
var worldCoordinate = projection.fromLatLngToPoint(chicago);
var pixelCoordinate = new google.maps.Point(
worldCoordinate.x * numTiles,
worldCoordinate.y * numTiles);
(you can view source with firebug or similar).
If you dont have it (in mozilla: Tools -> web developer -> page source)
Hopefully, this will help you get through the problem :)
I'm a bit new to canvas and such so forgive if it's a trivial question.
I'd like to be able to animate an object following a path (defined as bezier path) but I'm not sure how to do it.
I've looked at Raphael but I can't work out how to follow the path over time.
Cake JS looked promising in the demo, but I'm really struggling the documentation, or lack thereof in this case.
Has anyone got some working example of this?
Use the code on my website from this related question, but instead of changing the .style.left and such in the callback, erase and re-draw your canvas with the item at the new location (and optionally rotation).
Note that this uses SVG internally to easily interpolate points along a bézier curve, but you can use the points it gives you for whatever you want (including drawing on a Canvas).
In case my site is down, here's a current snapshot of the library:
function CurveAnimator(from,to,c1,c2){
this.path = document.createElementNS('http://www.w3.org/2000/svg','path');
if (!c1) c1 = from;
if (!c2) c2 = to;
this.path.setAttribute('d','M'+from.join(',')+'C'+c1.join(',')+' '+c2.join(',')+' '+to.join(','));
this.updatePath();
CurveAnimator.lastCreated = this;
}
CurveAnimator.prototype.animate = function(duration,callback,delay){
var curveAnim = this;
// TODO: Use requestAnimationFrame if a delay isn't passed
if (!delay) delay = 1/40;
clearInterval(curveAnim.animTimer);
var startTime = new Date;
curveAnim.animTimer = setInterval(function(){
var now = new Date;
var elapsed = (now-startTime)/1000;
var percent = elapsed/duration;
if (percent>=1){
percent = 1;
clearInterval(curveAnim.animTimer);
}
var p1 = curveAnim.pointAt(percent-0.01),
p2 = curveAnim.pointAt(percent+0.01);
callback(curveAnim.pointAt(percent),Math.atan2(p2.y-p1.y,p2.x-p1.x)*180/Math.PI);
},delay*1000);
};
CurveAnimator.prototype.stop = function(){
clearInterval(this.animTimer);
};
CurveAnimator.prototype.pointAt = function(percent){
return this.path.getPointAtLength(this.len*percent);
};
CurveAnimator.prototype.updatePath = function(){
this.len = this.path.getTotalLength();
};
CurveAnimator.prototype.setStart = function(x,y){
var M = this.path.pathSegList.getItem(0);
M.x = x; M.y = y;
this.updatePath();
return this;
};
CurveAnimator.prototype.setEnd = function(x,y){
var C = this.path.pathSegList.getItem(1);
C.x = x; C.y = y;
this.updatePath();
return this;
};
CurveAnimator.prototype.setStartDirection = function(x,y){
var C = this.path.pathSegList.getItem(1);
C.x1 = x; C.y1 = y;
this.updatePath();
return this;
};
CurveAnimator.prototype.setEndDirection = function(x,y){
var C = this.path.pathSegList.getItem(1);
C.x2 = x; C.y2 = y;
this.updatePath();
return this;
};
…and here's how you might use it:
var ctx = document.querySelector('canvas').getContext('2d');
ctx.fillStyle = 'red';
var curve = new CurveAnimator([50, 300], [350, 300], [445, 39], [1, 106]);
curve.animate(5, function(point, angle) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillRect(point.x-10, point.y-10, 20, 20);
});
In action: http://jsfiddle.net/Z2YSt/
So, here is the verbose version:
t being any number between 0 and 1 representing time; the p0, p1, p2, p3 objects are the start point, the 1st control point, the 2nd control point an the end point respectively:
var at = 1 - t;
var green1x = p0.x * t + p1.x * at;
var green1y = p0.y * t + p1.y * at;
var green2x = p1.x * t + p2.x * at;
var green2y = p1.y * t + p2.y * at;
var green3x = p2.x * t + p3.x * at;
var green3y = p2.y * t + p3.y * at;
var blue1x = green1x * t + green2x * at;
var blue1y = green1y * t + green2y * at;
var blue2x = green2x * t + green3x * at;
var blue2y = green2y * t + green3y * at;
var finalx = blue1x * t + blue2x * at;
var finaly = blue1y * t + blue2y * at;
Here is a ball using <canvas> following a path in JSfiddle
The names of the variables come from this gif wich is the best explication for bezier curves: http://en.wikipedia.org/wiki/File:Bezier_3_big.gif
A short version of the code, inside a function ready to copy/paste:
var calcBezierPoint = function (t, p0, p1, p2, p3) {
var data = [p0, p1, p2, p3];
var at = 1 - t;
for (var i = 1; i < data.length; i++) {
for (var k = 0; k < data.length - i; k++) {
data[k] = {
x: data[k].x * at + data[k + 1].x * t,
y: data[k].y * at + data[k + 1].y * t
};
}
}
return data[0];
};
Related stuff:
http://blogs.sitepointstatic.com/examples/tech/canvas-curves/bezier-curve.html
http://13thparallel.com/archive/bezier-curves/
http://gsgd.co.uk/sandbox/jquery/easing/jquery.easing.1.3.js
http://www.youtube.com/watch?v=hUCT4b4wa-8
I wouldn't use Canvas for this unless you really have to. SVG has animation along a path built in. Canvas requires quite a bit of math to get it working.
Here's one example of SVG animating along a path.
Here's some discussion about it for raphael: SVG animation along path with Raphael
Please note that Raphael uses SVG and not HTML5 Canvas.
One way to animate along a bezier path in Canvas is to continuously bisect the bezier curve, recoring the midpoints until you have a lot of points (say, 50 points per curve) that you can animate the object along that list of points. Search for bisecting beziers and similar queries for the related math on that.
I have a point (X,Y) and I want to create a square , Google maps LatLngBounds object so to make geocode requests bias only into this LatLngBound region.
How can I create such a LatLngBounds square with center the given point? I have to find the NE and SW point. But how can I find it given a distance d and a point (x,y)?
Thanks
You can also getBounds from a radius defined as a circle and leave the trig to google.
new google.maps.Circle({center: latLng, radius: radius}).getBounds();
well that's very complicated. for a rough box try this:
if (typeof(Number.prototype.toRad) === "undefined") {
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
}
if (typeof(Number.prototype.toDeg) === "undefined") {
Number.prototype.toDeg = function() {
return this * 180 / Math.PI;
}
}
var dest = function(lat,lng,brng, dist) {
this._radius = 6371;
dist = typeof(dist) == 'number' ? dist : typeof(dist) == 'string' && dist.trim() != '' ? +dist : NaN;
dist = dist / this._radius;
brng = brng.toRad();
var lat1 = lat.toRad(),
lon1 = lng.toRad();
var lat2 = Math.asin(Math.sin(lat1) * Math.cos(dist) + Math.cos(lat1) * Math.sin(dist) * Math.cos(brng));
var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(dist) * Math.cos(lat1), Math.cos(dist) - Math.sin(lat1) * Math.sin(lat2));
lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
return (lat2.toDeg() + ' ' + lon2.toDeg());
}
var northEastCorner = dest(centreLAT,centreLNG,45,10);
var southWestCorner = dest(centreLAT,centreLNG,225,10);
EDIT
The above was they way to do it way back in 2011 when I wrote it. These days the google maps api has come on a loooong way. The answer by #wprater is much neater and uses some of the newer api methods.
Wouldn't it work to simply add/subtract d/2 to your x/y locations?
Given x,y as the center point:
NW = x-(d/2),y-(d/2)
SE = x+(d/2),y+(d/2)
Don't trust me on this, though - I am terrible at math :)
This assumes d as a "diameter", rather than a radius. If "d" is the radius, don't bother with the divide-by-two part.
I'm trying to animate some bitmaps out in relation to a center point. They don't all start at that center point, but I want them to fly out as though a force from that center point slammed into them and pushed them outwards radially, such that they fly completely off the stage.
So: I know the center point, and the x and y position of each bitmap arranged around it. For each one I can draw a line from the center to that x,y point. I should then be able to get the angle formed by that line to the horizontal, and then set a destination point farther out on that line. The bitmap will be tweened out to that point. I believe that that is what Math.atan2 is for.
Here's what I've got as I iterate through the array of bitmaps (i is an object):
var angle:Number = Math.atan2(i.bitmap.y - centerY, i.bitmap.x - centerX) * 180 / Math.PI;
var dist:Number = 200; //arbitrary number, just to test
destX = centerX + dist * Math.cos(angle); //destination x
destY = centerY + dist * Math.sin(angle); //destination y
Instead of these things gliding out radially, they're jumping around.
I'm having trouble understanding atan2 and exactly what I'm doing wrong.
Thanks,
David
You can achieve the same effect without trigonometric functions using just vector operations:
var dist:Number = 200; //arbitrary number, just to test
var dx:Number = i.bitmap.x - centerX;
var dy:Number = i.bitmap.y - centerY;
var length:Number = Math.sqrt( dx*dx + dy*dy );
var normalizeddx:Number = dx / length;
var normalizeddy:Number = dy / length;
destX = centerX + dist * normalizeddx; //destination x
destY = centerY + dist * normalizeddy; //destination y
This should be much faster, than using trigonometric functions. I don't know the language specifics of actionscript, so probably this can be optimized more.
Try removing the *180/PI to keep the angle in radians.
var angle:Number = Math.atan2(i.bitmap.y-centerY, i.bitmap.x - centerX);
Then change destX and destY to
destX = i.bitmap.x + dist * Math.cos(angle);
destY = i.bitmap.y + dist * Math.sin(angle);
atan2 could work in this situation I suppose but I would just use atan:
var angle:Number = Math.atan((i.bitmap.y - centerY) / (i.bitmap.x - centerX));
ADDITION:
Code I just saw on another forum that appears to do what you want (there's only a slight difference from what you wrote in the first place)
var angle:Number = Math.atan2(mouseX,mouseY-180)-Math.PI/2;
var xNew:Number = 20*Math.cos(angle);
var yNew:Number = -20*Math.sin(angle);
You have to get rid of the *180/Math.PI part. The angle has to be in radians. So the first line would look like
var angle:Number = Math.atan2(i.bitmap.y - centerY, i.bitmap.x - centerX);
The rest should be fine.