I'm trying to write a code which let my users define some points on the map and once they created a point, the program should draw a circle with defined diameter(in kilometers or ... ) around the point.
I can draw a point but I don't know how I could handle what I said.
Here is an example about what I want:
Use the following function to create circular points around a point.
//pass the
//#pointX,
//#pointY,
//#radius of circle (in your case diameter/2)
//#pointsToFind this is how detail you want the circle to be (360 if you want one point for each rad)
function createCirclePointCoords(circleCenterX,circleCenterY,circleRadius,pointsToFind){
var angleToAdd = 360/pointsToFind;
var coords = [];
var angle = 0;
for (var i=0;i<pointsToFind;i++){
angle = angle+angleToAdd;
console.log(angle);
var coordX = circleCenterX + circleRadius * Math.cos(angle*Math.PI/180);
var coordY = circleCenterY + circleRadius * Math.sin(angle*Math.PI/180);
coords.push([coordX,coordY]);
}
return coords;
}
And then create a polygon out of the coordinates returned from the function
var circleCoords = createCirclePointCoords(0,0,1000,360);
var geom = new ol.geom.Polygon([
circleCoords
])
Related
the method getRadius() of class ol.geom.Circle returns the radius of a circle ;
how can I convert this value into meters ?
I draw a circle on a map using a particular projection (e.g Spherical Mercator, LambertIIe, etc.)
then I convert this geometry into degree to process further treatments such as testing if a point (in degrees) is inside this circle
so getting the radius always in the same unit (meter) from a geometry in degree would be useful
thanks in advance
Jean-Marie
You can use the ol.proj.METERS_PER_UNIT table to get the radius in meters:
var units = map.getView().getProjection().getUnits();
var radiusM = circle.getRadius() * ol.proj.METERS_PER_UNIT[units];
I use the following method to get the radius (getFirstCoordinate referring to the center and getLastCoordinate referring to a point on the perimeter) :
var wgs84Sphere = new ol.Sphere(6378137);
var center=circle.getFirstCoordinate();
var ray=getDistance(center[0],center[1],circle.getLastCoordinate()[0],circle.getLastCoordinate()[1]);
// getDistance returns the distance between 2 points (source : http://openlayers.org/en/v3.7.0/examples/measure.html)
function getDistance(long1,lat1,long2,lat2) {
var c1 = [long1,lat1];
var c2 = [long2,lat2];
return wgs84Sphere.haversineDistance(c1,c2);
}
I'm using Metal and I want the user to be able to tap certain objects onscreen. I'm using a tap gesture recognizer, and so I have a CGPoint, but I don't know how to find the object at that point. Is there either a way to get the onscreen coordinates for metal, or a way to transform the CGPoint into the metal coordinate space?
I ended up doing this with a raytracing-type technique. I used a bounding sphere for each object and calculated whether or not the ray intersected it and how far from the ray's origin the intersection was, with the ray being from the camera through the onscreen point of clicking. The code is as follows:
var location = gestureRecognizer.locationInView(self.view)
var proj_matrix = projectionMatrix.copy()
var view_matrix = worldMatrix.copy()
var x = (2.0 * location.x) / self.view.bounds.size.width - 1.0;
var y = 1.0 - (2.0 * location.y) / self.view.bounds.size.height;
var newLocation = simpleVertex(x: Float(x), y: Float(y), z: Float(-1.0))
var projSpaceLoc = GLKMatrix4MultiplyVector4(GLKMatrix4Invert(proj_matrix.matrix(), nil), GLKVector4Make(newLocation.x, newLocation.y, newLocation.z, 1.0))
projSpaceLoc = GLKVector4Make(projSpaceLoc.x, projSpaceLoc.y, -1.0, 0.0)
var viewSpaceLoc = GLKMatrix4MultiplyVector4(GLKMatrix4Invert(view_matrix.matrix(), nil), projSpaceLoc)
GLKVector4Normalize(viewSpaceLoc)
//tappable is an array of nodes that can be tapped
for node in tappable {
var r: Float = node.r //this is the radius of the bounding sphere for that node
var c: GLKVector4 = node.origin //this is the origin of the bounding sphere
var little_c = GLKVector4DotProduct(c, c) - powf(r, 2)
var b = GLKVector4DotProduct(viewSpaceLoc, c)
var discriminant = powf(b, 2) - little_c
//If the discriminant is positive, their are two points of intersection, if it is 0, one point, and if it is negative, no points.
if (discriminant >= 0) {
var t_1 = (Float(-1.0)*b) + sqrtf(discriminant)
var t_2 = (Float(-1.0)*b) - sqrtf(discriminant)
if (intersect != nil) {
if (min(t_1, t_2) < intersect.intersect_point) {
intersect = (node, min(t_1, t_2))
}
} else {
intersect = (node, min(t_1, t_2))
}
}
}
I am using getLength to retrieve the linestring length.
For the same segment:
1- when using google map measure tool, I get 228m
2- when using IGN geoportail measure tool, I get 228m
3- when I use e.feature.getGeometry().getLength() I get 330m
Here are the flat coordinates:
e.feature.getGeometry().getFlatCoordinates() :
[571382.4214041593, 5723486.068714521, 571593.8175605105, 5723741.65502785]
in 4326:
[5.132815622245775, 45.644023326845485, 5.134714626228319, 45.64562844964627]
When I check the coordinates position on either ol3 or google map, I get the same points. The difference must come from the calcul...
Did I miss something and I should not use the getLength method? Please give me some direction if you think this is not an issue.
geometry.getLength() returns the length in the map view projection, which is usually spherical mercator. Spherical mercator distances are stretched at a rate of 1/cos(latitude); in your example: 228/330 ~ cos(45.64).
To get the real spherical distance:
var geometry = feature.getGeometry();
alert (geometry.getLength());
// get points of geometry (for simplicity assume 2 points)
var coordinates = geometry.getCoordinates();
// transform points from map projection (usually spherical mercator) to WGS84
var mapProjection = map.getView().getProjection();
var t1 = ol.proj.transform(coordinates[0], mapProjection, 'EPSG:4326');
var t2 = ol.proj.transform(coordinates[1], mapProjection, 'EPSG:4326');
// create sphere to measure on
var wgs84sphere = new ol.Sphere(6378137); // one of WGS84 earth radius'
// get distance on sphere
var dist = wgs84sphere.haversineDistance(t1, t2);
alert (dist);
For even higher accuracy you have to measure on the WGS84 ellipsoid instead of the sphere.
The above answer is correct, allthough if you are trying to get the length of a LinePoint with several positions it will only calculate between the first ones.
Here is a small addition on working with LinePoints with several positions:
var geometry = feature.getGeometry();
// get points of geometry (for simplicity assume 2 points)
var coordinates = geometry.getCoordinates();
// transform points from map projection (usually spherical mercator) to WGS84
var mapProjection = map.getView().getProjection();
// create sphere to measure on
var wgs84sphere = new ol.Sphere(6378137); // one of WGS84 earth radius'
var dist = 0;
//loop through all coordinates
for(var i = 0; i < coordinates.length -1; i++) {
var t1 = ol.proj.transform(coordinates[i], mapProjection, 'EPSG:4326');
var t2 = ol.proj.transform(coordinates[i+1], mapProjection, 'EPSG:4326');
// get distance on sphere
dist += wgs84sphere.haversineDistance(t1, t2);
}
alert(dist);
I am trying to update the current rotation (and sometimes the position) of a CALayer.
What I am trying to in a couple of simple steps:
Store a couple of CALayers in an array, so I can reuse them
Set the anchor point of all CALayers to 0,0.
Draw CALayer objects where the object starts at a position on a circle
The layers are rotated by the same angle as the circle at that position
Update the position and rotation of the CALayer to match new values
Here is a piece of code I have:
lineWidth is the width of a line
self.items is an array containing the CALayer objects
func updateLines() {
var space = 2 * M_PI * Double(circleRadius);
var spaceAvailable = space / (lineWidth)
var visibleItems = [Int]();
var startIndex = items.count - Int(spaceAvailable);
if (startIndex < 0) {
startIndex = 0;
}
for (var i = startIndex; i < self.items.count; i++) {
visibleItems.append(self.items[i]);
}
var circleCenter = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
/* Each line should move up and rotate accordin to this value */
var anglePerLine: CGFloat = (360 / CGFloat(visibleItems.count)).toRadians()
/* Starting position, 270 degrees is on top */
var startAngle: CGFloat = CGFloat(270).toRadians();
/* Lines default rotation, we rotate it to get the right side up */
var lineAngle: CGFloat = CGFloat(180).toRadians();
for (var itemIndex = 0; itemIndex < visibleItems.count; itemIndex++) {
var itemLayer = self.itemLayers[itemIndex];
itemLayer.opacity = 1 - ((0.9 / visibleItems.count) * itemIndex);
/* Calculate start position of layer */
var x = CGFloat(circleRadius) * cos(startAngle) + CGFloat(circleCenter.x);
var y = CGFloat(circleRadius) * sin(startAngle) + CGFloat(circleCenter.y);
var height = CGFloat((arc4random() % 80) + 10);
/* Set position and frame of layer */
itemLayer.frame = CGRectMake(CGFloat(x), CGFloat(y), CGFloat(lineWidth), height);
itemLayer.position = CGPointMake(CGFloat(x), CGFloat(y));
var currentRotation = CGFloat((itemLayer.valueForKeyPath("transform.rotation.z") as NSNumber).floatValue);
var newRotation = lineAngle - currentRotation;
var rotationTransform = CATransform3DRotate(itemLayer.transform, CGFloat(newRotation), 0, 0, 1);
itemLayer.transform = rotationTransform;
lineAngle += anglePerLine;
startAngle += anglePerLine;
}
}
The result of the first run is exactly as I want it to be:
The second run through this code just doesn't update the CALayers correctly and it starts to look like this:
I think it has to do with my code to update the location and transform properties of the CALayer, but whatever I do, it always results in the last picture.
Answered via Twitter: setting frames and transform is mutually exclusive. Happy to help. Finding my login credentials for SO is harder. :D
Found the answer thanks to #iosengineer on Twitter. When setting a position on the CALayer, you do not want to update the frame of the layer, but you want to update the bounds.
Smooth animation FTW
I create stage walls and a box inside on my mobile app using starling + as3.
Ok, now when I test the app the box falls but it does not match the walls, as if there
was an offset:
https://www.dropbox.com/s/hd4ehnfthh0ucfm/box.png
Here is how I created the boxes (walls and the box).
It seems like there is an offset hidden, what do you think?
public function createBox(x:Number, y:Number, width:Number, height:Number, rotation:Number = 0, bodyType:uint = 0):void {
/// Vars used to create bodies
var body:b2Body;
var boxShape:b2PolygonShape;
var circleShape:b2CircleShape;
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.shape = boxShape;
fixtureDef.friction = 0.3;
// static bodies require zero density
fixtureDef.density = 0;
var quad:Quad;
bodyDef = new b2BodyDef();
bodyDef.type = bodyType;
bodyDef.position.x = x / WORLD_SCALE;
bodyDef.position.y = y / WORLD_SCALE;
// Box
boxShape = new b2PolygonShape();
boxShape.SetAsBox(width / WORLD_SCALE, height / WORLD_SCALE);
fixtureDef.shape = boxShape;
fixtureDef.density = 0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
// create the quads
quad = new Quad(width, height, Math.random() * 0xFFFFFF);
quad.pivotX = 0;
quad.pivotY = 0;
// this is the key line, we pass as a userData the starling.display.Quad
bodyDef.userData = quad;
//
body = m_world.CreateBody(bodyDef);
body.CreateFixture(fixtureDef);
body.SetAngle(rotation * (Math.PI / 180));
_clipPhysique.addChild(bodyDef.userData);
}
The SetAsBox method takes half width and half height as its parameters. I'm guessing your graphics don't match your box2d bodies. So either you will need to make your graphics twice as big or multiply your SetAsBox params by 0.5. Also the body pivot will be in the center of it, so offset your movieclip accordingly depending on its pivot position.
Note that box2d has a debugrenderer which can outline your bodies for you to see what's going on.