I am drawing a circle and inside it a line that shows its radius , i use same line coordinates , but as result i get a smaller circle , any help ???
function DrawLine() {
var lineCoordinates = [[3210202.3139208322, 5944966.311907868], [3075978.8922520624, 6055647.128864803]];
var line = new ol.geom.LineString(lineCoordinates);
var feature = new ol.Feature(line);
var id = guid();
feature.featureID = id;
feature.setProperties({
'id': id,
'name': typeSelect.value,
'description': 'Some values'
})
source.addFeature(feature);
};
function DrawCircle() {
var sourceProj = map.getView().getProjection();
var wgs84Sphere = new ol.Sphere(6378137);
var c1 = ol.proj.transform([3210202.3139208322, 5944966.311907868], sourceProj, 'EPSG:4326');
var c2 = ol.proj.transform([3075978.8922520624, 6055647.128864803], sourceProj, 'EPSG:4326');
var distance = wgs84Sphere.haversineDistance(c1, c2);
var point = new ol.geom.Circle([3210202.3139208322, 5944966.311907868],distance,'XY');
var feature = new ol.Feature(point);
console.log(distance);
var id = guid();
feature.featureID = id;
feature.setProperties({
'id': id,
'name': typeSelect.value,
'description': 'Some values'
})
source.addFeature(feature);
};
Your code looks pretty intense. If the radius is just for looks, why not just go with something simple along the lines of this:
function drawRadius(circle_, direction_){
context.moveTo(circle_.center_x, circle_.center_y);
context.lineTo(circle_.center_x + Math.cos(direction_) * circle_.radius, circle_.center_y + Math.sin(direction_) * circle_.radius);
context.stroke();
}
Where direction is maybe the rotation of the circle in radians: 0 to 2*PI,
and the context is a canvasRenderingContext2D.
Your circle generator could look like this:
function getCircleFromPoints(point_a_, point_b_){
var distance_x=point_b_.x-point_a_.x;
var distance_y=point_b_.y-point_a_.y;
var circle={
center_x:point_a_.x;
center_y:point_a_.y;
radius:Math.sqrt(distance_x*distance_x+distance_y*distance_y);
};
return circle;
}
This will put your circle's center at point_a_ and its edge at point_b_. It's radius will be equal to the distance between the two points.
I realize that this is all plain JavaScript, but the concept remains the same. Use the distance formula to get the radius of the circle equal to the distance between the two points and set the circle's center to one of the points.
You set the radius of the circle to the real distance between the two points, not the projected distance. Since ol3 works on the projected plane, those might be different for many projections.
I wrote more in depth about the difference between projected and real radii in ol3 in this answer.
Related
I would like to have an option to export my drawings as geoJSON files. So far I've managed perfectly with all of them except for the circle, which comes admittedly with no geometries at all!
I know, that GeoJSON format can render a pure circle, and we need to model it as the points as per this thread:
How to define a circle using GeoJson?
https://docs.mongodb.com/manual/tutorial/query-a-2dsphere-index/
I am fully aware of it, that's why I started to modify my code in order to make the circle physically visible.
My problem is very similar to this one:
Can't save features of drawn Circle to JSON in Openlayers 3
and similar to the solution below:
http://geoadmin.github.io/ol3/apidoc/sphere.js.html
After amending the code:
var wgs84Sphere = new ol.sphere(6378137);
var circleInteraction = new ol.interaction.Draw({
geometryFunction: function(coordinates, geometry) {
if (!geometry) {
geometry = new ol.geom.Polygon(null);
}
var center = coordinates[0];
var last = coordinates[1];
var dx = center[0] - last[0];
var dy = center[1] - last[1];
var radius = Math.sqrt(dx * dx + dy * dy);
var circle = ol.geom.Polygon.circular(wgs84Sphere, ol.proj.toLonLat(center), radius);
circle.transform('EPSG:4326', 'EPSG:3857');
geometry.setCoordinates(circle.getCoordinates());
return geometry;
},
type: 'Circle',
source: vectorLayer.getSource()
});
circleInteraction.setActive(false);
circleInteraction.on('drawend', onDrawend );
I have received an error:
Uncaught TypeError: ol.sphere is not a constructor
which is caused by the OpenLayers library upgrade and is not been valid anymore since version 5.0.0.
https://github.com/openlayers/openlayers/issues/9046
regarding this situation, I've tried to change the wgs84Sphere variable
var polygon_geometry = new ol.geom.Polygon;
var wgs84Sphere = ol.sphere.getArea(polygon_geometry, projection='EPSG:4326', radius=6378137)
but it didn't work either
Uncaught TypeError: Cannot read properties of undefined (reading SimpleGeometry.js:170
pointing the line
if (coordinates.length === 0) {
Is it possible to generate the .geoJSON geometries for circle then?
My full JSFiddle is here:
https://jsfiddle.net/pet6h30d/
The parameters for ol.geom.Polygon.circular have also changed so you do not need to construct a sphere, see https://openlayers.org/en/latest/examples/draw-and-modify-geodesic.html If you are not need to modify you only need a polygon
geometryFunction: function(coordinates, geometry, projection) {
if (!geometry) {
geometry = new ol.geom.Polygon([]);
}
var center = ol.proj.transform(coordinates[0], projection, 'EPSG:4326');
var last = ol.proj.transform(coordinates[1], projection, 'EPSG:4326');
var radius = ol.sphere.getDistance(center, last);
var circle = ol.geom.Polygon.circular(center, radius);
circle.transform('EPSG:4326', projection);
geometry.setCoordinates(circle.getCoordinates());
return geometry;
},
The mxgraph Google Maps example (and demo) shows a simple implementation that create a Google Map overlay.
However I cannot figure out the correlation between the vertex pixel co-ordinates and the equivalent lat/long co-ordinates.
Line 140 of the example shows some hard-coded pixel co-ordinates (23,23) for Seattle:
var n1 = graph.insertVertex(parent, null, {label:'Seattle'}, 23, 23, 10, 10);
I want to display a geographic data set (i.e. I have latitude/longitude) but I can not figure out the conversion to mxGraph vertex pixel co-ordinates.
The Google API's "fromLatLngToDivPixel" does not work (it returns the lat/long of the centre of the map as 0,0), neither does "fromLatLngToContainerPixel".
Even weirder, a vertex with co-ordinates of 0,0 does not appear in the top left corner of the map (it is a little inside the map).
So it's a little complicated.
I finally created two classes to help out:
// Represent a map pixel
class PixelPos {
constructor(x,y) {
this._x = x;
this._y = y;
}
get x(){
return this._x;
}
get y(){
return this._y;
}
}
// Handles converting between lat/log and pixels
// Based on the initial created map div container.
class CoordTranslator {
constructor(graph, projection, bounds) {
this._graph = graph;
this._projection = projection;
this._bounds = bounds;
//
var swb = this._bounds.getSouthWest();
var neb = this._bounds.getNorthEast();
this._neb_lat = neb.lat();
this._swb_lng = swb.lng();
this._map_width = neb.lng() - swb.lng();
this._map_height = neb.lat() - swb.lat();
// Get pixel values from the Geo boundary box
var sw = this._projection.fromLatLngToDivPixel(swb);
var ne = this._projection.fromLatLngToDivPixel(neb);
// Map pixel width and Height
this._pix_width = (ne.x - sw.x);
this._pix_height = (sw.y - ne.y);
this._scale = graph.view.getScale();
}
// Convert the provided lat/long into a PixelPos
convert(lat, lng){
var x = (lng-this._swb_lng)/this._map_width;
x = x*this._pix_width;
x = x/this._scale;
var y = (this._neb_lat - lat)/this._map_height;
y = y*this._pix_height;
y = y/this._scale;
// And, for some unknown reason, I have to magic a final offset!!
return new PixelPos(x-5,y-3);
}
}
I initialise the CoordTranslator class in the mxGraphOverlay.prototype.draw:
mxGraphOverlay.prototype.draw = function()
{
if (drawn = 0){
drawn = 1;
....
let ct = new CoordTranslator(this.graph_, overlayProjection, this.bounds_);
....
// 6193351 # 46.8127123,14.5866472
var pp = ct.convert(46.8127123, 14.58664720);
var n3 = graph.insertVertex(parent, null, {label:'6193351'}, pp.x, pp.y, 10, 10);
....
}
}
There are probably better ways to initialise and use the CoordTranslator, but the ideas are here.
I am supposed to plot the well deviation surveys on a 3D grid. With the help of a few articles on the web, I have accomplished a 3D grid with required size. The current problem I am facing right now is that the labels for x,y and z axis are attached to the grid, rather they are misplaced on the scene.
var labelsH = labelAxis(height, data.labels.y,"y");
labelsH.position.x = width;
labelsH.position.y = - height +(2*height/a)-20;
labelsH.position.z = depth;
scene.add(labelsH);
function labelAxis(width, data, direction){
var separator = 2*width/data.length,
p = {
x:0,
y:0,
z:0
},
dobj = new THREE.Object3D();
for ( var i = 0; i < data.length; i ++ ) {
var label = makeTextSprite(data[i]);
label.position.set(p.x,p.y,p.z);
dobj.add( label );
if (direction=="y"){
p[direction]+=separator;
}else{
p[direction]-=separator;
}
//console.log(p.x+":"+p.y+":"+p.z)
}
return dobj;
}
See the https://jsfiddle.net/3tw3dt1u/ for full code example.
Further more, the data that I need to plot is already in the jsFiddle mentioned above. Having minimal javascript skills, I have no idea how this data will be plotted on the grid to form something like this:
see image for required result
Thanks a lot in advance for any help.
Your question is regarding the plotting of the points, this is how it could be done:
JSFiddle working example
The key points are below.
// Not necessary, but I prefer to use the same scale as they use in their example. It's also easier since the data is according to those scales.
var graphDimensions = {
w:3000,
d:3000,
h:7000
};
var vectors = realData.map(function(d) { // Create vectors from your data
return new THREE.Vector3(d[0], d[1], d[2]);
});
var curve = new THREE.CatmullRomCurve3(vectors); // Create a curve with the vectors created above
var material = new THREE.LineBasicMaterial({color: "blue"}); // Material for the curve
var geometry = new THREE.Geometry();
var splinePoints = curve.getPoints(5000); // The 5000 in here is the resolution (number of points) on the curve. Increase for a smoother curve, decrease for a more jagged curve.
for (var i = 0; i < splinePoints.length; i++) { // Loop through the points to create vertices in the geometry
geometry.vertices.push(splinePoints[i]);
}
var line = new THREE.Line(geometry, material); // Create a line with your geometry and material
scene.add(line); // Add it to the scene
line.rotateX(Math.PI / 2); // Orient the line correctly
boundingGrid.position.set(0, -3.5, 1.5); // Move the grid into position
boundingGrid.scale.set(0.001, 0.001, 0.001); // Reduce by whatever scale you want to decrease it in size (otherwise you have to scroll out forever)
line.scale.set(0.001, 0.001, 0.001);
On Leaflet I can create a new circle easily given the centre and the radius:
// Circle
var radius = 500; // [metres]
var circleLocation = new L.LatLng(centreLat, centreLon);
var circleOptions = {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5
};
var circle = new L.Circle(circleLocation, radius, circleOptions);
map.addLayer(circle);
The circle above is created and drawn without problems, so it is all.
However, if I wanted now to create and draw a rectangle that which bounds the circle, it does not work. Here is what I did:
// Rectangle
var halfside = radius; // It was 500 metres as reported above
// convert from latlng to a point (<-- I think the problem is here!)
var centre_point = map.latLngToContainerPoint([newCentreLat, newCentreLon]);
// Compute SouthWest and NorthEast points
var sw_point = L.point([centre_point.x - halfside, centre_point.y - halfside]);
var ne_point = L.point([centre_point.x + halfside, centre_point.y + halfside]);
// Convert the obtained points to latlng
var sw_LatLng = map.containerPointToLatLng(sw_point);
var ne_LatLng = map.containerPointToLatLng(ne_point);
// Create bound
var bounds = [sw_LatLng, ne_LatLng];
var rectangleOptions = {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5
};
var rectangle = L.rectangle(bounds, rectangleOptions);
map.addLayer(rectangle);
The size of the rectangle that I obtain has nothing to do with 500 metres. Also, it looks like the size of the rectangle depends on the zoom level the map is. None of these problems arose for the circle.
I suspect the way I transform the latitude/longitude to point and viceversa is wrong.
Just use the getBounds method that L.Circle inherits from L.Path:
Returns the LatLngBounds of the path.
http://leafletjs.com/reference.html#path-getbounds
var circle = new L.Circle([0,0], 500).addTo(map);
var rectangle = new L.Rectangle(circle.getBounds()).addTo(map);
Working example on Plunker: http://plnkr.co/edit/n55xLOIohNMY6sVA3GLT?p=preview
I was getting "Cannot read property 'layerPointToLatLng' of undefined" error, So I made some changes to iH8's answer.
var grp=L.featureGroup().addTo(map);
var circle=L.circle([0,0],{radius:<circle radius>}).addTo(grp);
L.rectangle(circle.getBounds()).addTo(this.bufferMap);
map.removeLayer(grp);
I have five rectangles placed at different points along a circle like this - http://imgur.com/uVYkwl7.
Upon clicking any rectangle i want the circle to move to the left of the screen, gradually scaling down it's radius until the circle's center reaches x=0. I'd like the five rectangles to move along with the circle while its being scaled down and also adjust their own positions and scale on the circle so that they are within the view's bounds, like this - http://imgur.com/acDG0Aw
I'd appreciate any help on how to go about doing this. Heres my code for getting to the 1st image and animating the circle:
var radius = 300;
var center = view.center;
var circle = new Path.Circle({
center: view.center,
radius: radius,
strokeColor: 'black',
name: 'circle'
});
var path = new Path.Rectangle({
size: [230, 100],
fillColor: '#1565C0'
});
var rectText = ['Text 1',
'Text 2',
'Text 3',
'Text 4',
'Text 5'
];
var symbol = new Symbol(path);
var corners = [
new Point(center.x, center.y - radius),
new Point(center.x - radius, center.y - radius / 2),
new Point(center.x + radius, center.y - radius / 2),
new Point(center.x - radius, center.y + radius / 2),
new Point(center.x + radius, center.y + radius / 2)
];
var rectClicked = false;
var clickedRect = null;
var rectClick = function(event) {
rectClicked = true;
clickedRect = this;
};
function onFrame(event) {
// Your animation code goes in here
if (rectClicked) {
for (var i = 0; i < 1; i++) {
var item = project.activeLayer.children[i];
if (item.name == 'circle') {
if (item.position.x < 0) {
rectClicked = false;
} else {
item.position.x -= 10;
item.scale(1/1.01);
}
}
}
}
}
// Place the instances of the symbol:
for (var i = 0; i < corners.length; i++) {
var placedSymbol = symbol.place(corners[i]);
placedSymbol.onMouseDown = rectClick;
var rText = new PointText({
point: placedSymbol.bounds.topLeft + 20,
content: rectText[i],
fontSize: '20',
fillColor: 'white'
});
}
Paper.js provides rotations around a pivot out of the box.
var pivotPoint = new Point(10, 5);
circle.rotate(30,pivotPoint);
Here is the docs reference for this behaviour and here is a very basic Sketch example to illustrate this
The above snippet will rotate a circle(you can change this to rectangle in your case) by 30 degrees around a pivot point at coordinates 10,5 on the x/y axis.
Thus what you describe is certainly doable as long as the path that your elements will follow is always circular.
Bear in mind that in order for the pivot rotation to work the way you want them to you need to update the pivotPoint and reinitiate the rotation again.
Note: In case you want to move along an arbitrary shape instead of circular path, you should search for Paper.js animation-along-a-path which is something that I've seen been done before without much difficulty - e.g this simple Sketch by the creator of Paper.js himself.
The sketch I provided above is a basic example of rotation around a pivot point.
I'm dumping the Sketch code here in case the link goes dead:
//Create a center point
var centerCircle = new Path.Circle(paper.view.center, 100);
centerCircle.strokeColor = 'black';
centerCircle.dashArray = [10, 12];
//Create the circles
var circle1Radius = 30;
var circle1 = new Path.Circle((centerCircle.position-centerCircle.bounds.width/2)+circle1Radius, circle1Radius);
circle1.fillColor = '#2196F3';
var circle2Radius = 40;
var circle2 = new Path.Circle((centerCircle.position-centerCircle.bounds.width/2)+circle2Radius, circle2Radius);
circle2.fillColor = '#E91E63';
var circle3Radius = 40;
var circle3 = new Path.Circle((centerCircle.position-centerCircle.bounds.width/2)+circle2Radius, circle2Radius);
circle3.fillColor = '#009688';
var i=0;
var animationGap = 125; //how long to move before animating the next circle
var rotationSpeed = 2;
function onFrame(event) {
circle1.rotate(rotationSpeed,centerCircle.position);
if(i>animationGap)
circle2.rotate(rotationSpeed,centerCircle.position);
if(i>animationGap*2)
circle3.rotate(rotationSpeed,centerCircle.position);
i++;
}