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;
},
Related
I want to extrude a shape along an extrudePath using THREE.ExtrudeBufferGeometry().
Basically this works fine, but the initial shape orientation is arbitrary, due to FrenetFrames as answered in this question.
The extrudePath is lying on one plane, so there will never be a problem regarding closing the Extrusion due to "wrong" orientation.
var myExtrudePath = new THREE.CatmullRomCurve3( <some THREE.Vector3() vectors> );
var shape = new THREE.Shape();
shape.lineTo( ... );
...
var extrudeSettings = { extrudePath: myExtrudePath };
var geometry = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings );
Is there a way to set the initial shape orientation manually?
EDIT 1
I would like to avoid editing the three.js-File. I found something here. Is it (still) possible to give normal-vectors or frames as options to the ExtrudeBufferGeometry() ?
I imagine something like:
var phi = Math.PI / 2;
var normal = new THREE.Vector3( Math.sin(phi), Math.cos(phi), 0);
var binormal = new THREE.Vector3( Math.cos(phi), Math.sin(phi), 0);
var frames = { normals: [normal, normal], binormals: [binormal, binormal] };
var extrudeSettings = {
steps: 100,
extrudePath: path,
frames: frames
}
var geometry = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings );
This does not work so far. I get an Error in three.js:2582:4 :
TypeError: v is undefined
copy
ExtrudeBufferGeometry.prototype.addShape
ExtrudeBufferGeometry.prototype.addShapeList
ExtrudeBufferGeometry
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 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.