openlayers-3 precompose, how to do layer clipping with a rectangular geometry - openlayers-3

I'm trying to make a simple app doing something similar to the Layer Spy Example, but instead of a circle flowing the mouse pointer, I would like to do the clipping based on a rectangle which is always centered in the map.
(preferably with "fixed" size" i.e. changes "extent" when zooming)
Any help appreciated
Thanks
Frode

You can do it like in the example that you are referring to. But instead of context.arc() use context.rect() with a fixed position to clip the layer. Something like:
imagery.on('precompose', function(event) {
var ctx = event.context;
var pixelRatio = event.frameState.pixelRatio;
ctx.save();
ctx.beginPath();
var x = ctx.canvas.width / 2 - 100;
var y = ctx.canvas.height / 2 - 100;
ctx.rect(x, y, 100, 100);
ctx.clip();
});
http://jsfiddle.net/eo1c1x78/

Related

How to get clicked element's position and id to change color and draw lines on it

I'm developing a basic window frame configurator. I splitted glasses in function below. I want to change color when i clicked and get the position of clicked glass to draw openin direction lines.
I tried to understand and implement Lavrton's method(https://codesandbox.io/s/0xj7zml2zl?file=/src/index.js) but i couldn't succeed.
function glassDraw(piece, frameWidth, frameHeight) {
var glassGroup = new Konva.Group();
for (i = 0; i <piece; i++) {
var glass = new Konva.Rect({
name: 'Rect',
x: padding + (frameWidth / piece) * i,
y: padding,
width: frameWidth / piece,
height: frameHeight - padding * 2,
fill: 'lightblue',
id: 'glass'+i,
});
glassGroup.add(glass);
}
glassGroup.find("Rect").on('click', function (e) {
// get id of the cube i drag
var clickedId = e.target.attrs.id;
$('#theId').html(clickedId);
})
return glassGroup;
}
When i use getelementbyid method with id of konva element("glass"+i), it returns null. I think i need to get as a konva element.
You have to create a click listener for all of your rectangles.
for (let rect of glassGroup.children) {
rect.on('click', () => {
console.log(rect.x(), rect.y()); // current position of the object
console.log(rect.id()); // log id of the object
rect.fill('green'); // set color to green
layer.batchDraw(); // update layer (batchDraw just for better performance .draw() would work to)
})
}
Make sure you always update the stage by either call stage.draw() or layer.draw() (or batchDraw() for better performance) after changing something, otherwise your stage will not show anything of what you do.
If something with this code don't work feel free to ask.
Have a nice day.

Konva - visualize custom hit area

Is there an easy way to visualize a custom hit area shape?
As described here
https://konvajs.github.io/docs/events/Custom_Hit_Region.html
the hitFunc attribute can be set to a function that uses the supplied context to draw a custom hit area / region. Something like this:
var star = new Konva.Star({
...
hitFunc: function (context) {
context.beginPath()
context.arc(0, 0, this.getOuterRadius() + 10, 0, Math.PI * 2, true)
context.closePath()
context.fillStrokeShape(this)
}
})
For debugging purposes, I would like an easy way to toggle visual rendering of the shape (circle in this case), eg by filling it yellow.
Thanks :)
Currently, there is no public API for that. But you still can add hit canvas into the page somewhere and see how it looks:
const hitCanvas = layer.hitCanvas._canvas;
document.body.appendChild(hitCanvas);
// disable absolute position:
hitCanvas.style.position = '';
http://jsbin.com/mofocagupi/1/edit?js,output
Or you can add hit canvas on top of the stage and apply an opacity to make scene canvases visible:
const hitCanvas = layer.hitCanvas._canvas;
stage.getContainer().appendChild(hitCanvas);
hitCanvas.style.opacity = 0.5;
hitCanvas.style.pointerEvents = 'none';
http://jsbin.com/gelayolila/1/edit?js,output

OpenLayers - lock rotation of box or rectangle geometry while modifying

Openlayers provides useful functions for drawing boxes and rectangles and also has ol.geom.Geometry.prototype.rotate(angle, anchor) for rotating a geometry around a certain anchor. Is it possible to lock the rotation of a box/rectangle while modifying it?
Using the OpenLayers example located here to draw a box with a certain rotation to illustrate the point:
I would like the box/rectangle to maintain its rotation while still being able to drag the sides longer and shorter. Is there a simple way to achieve this?
Answering with the solution I came up with.
First of all, add the feature(s) to a ModifyInteraction so you are able to modify by dragging the corners of the feature.
this.modifyInteraction = new Modify({
deleteCondition: eventsCondition.never,
features: this.drawInteraction.features,
insertVertexCondition: eventsCondition.never,
});
this.map.addInteraction(this.modifyInteraction);
Also, add event handlers upon the events "modifystart" and "modifyend".
this.modifyInteraction.on("modifystart", this.modifyStartFunction);
this.modifyInteraction.on("modifyend", this.modifyEndFunction);
The functions for "modifystart" and "modifyend" look like this.
private modifyStartFunction(event) {
const features = event.features;
const feature = features.getArray()[0];
this.featureAtModifyStart = feature.clone();
this.draggedCornerAtModifyStart = "";
feature.on("change", this.changeFeatureFunction);
}
private modifyEndFunction(event) {
const features = event.features;
const feature = features.getArray()[0];
feature.un("change", this.changeFeatureFunction);
// removing and adding feature to force reindexing
// of feature's snappable edges in OpenLayers
this.drawInteraction.features.clear();
this.drawInteraction.features.push(feature);
this.dispatchRettighetModifyEvent(feature);
}
The changeFeatureFunction is below. This function is called for every single change which is done to the geometry as long as the user is still modifying/dragging one of the corners. Inside this function, I made another function to adjust the modified rectangle into a rectangle again. This "Rectanglify"-function moves the corners which are adjacent to the corner which was just moved by the user.
private changeFeatureFunction(event) {
let feature = event.target;
let geometry = feature.getGeometry();
// Removing change event temporarily to avoid infinite recursion
feature.un("change", this.changeFeatureFunction);
this.rectanglifyModifiedGeometry(geometry);
// Reenabling change event
feature.on("change", this.changeFeatureFunction);
}
Without going into too much detail, the rectanglify-function needs to
find rotation of geometry in radians
inversely rotate with radians * -1 (e.g. geometry.rotate(radians * (-1), anchor) )
update neighboring corners of the dragged corner (easier to do when we have a rectangle which is parallel to the x and y axes)
rotate back with the rotation we found in 1
--
In order to get the rotation of the rectangle, we can do this:
export function getRadiansFromRectangle(feature: Feature): number {
const coords = getCoordinates(feature);
const point1 = coords[0];
const point2 = coords[1];
const deltaY = (point2[1] as number) - (point1[1] as number);
const deltaX = (point2[0] as number) - (point1[0] as number);
return Math.atan2(deltaY, deltaX);
}

384x384px tile to diplay at 384x384px on map (retina)

I serve TMS tile with in two flavours: 256px or 384px through renderd option scale=1.5.
With Openlayers 3, the only way I found to display these 384px tiles their original size is to transform the canvas context like this:
map.getViewport().getElementsByTagName('canvas')[0].getContext("2d").setTransform(1.5, 0, 0, 1.5, -w, -h);
I think it's not the proper way to go, so what would be the right one?
I played a bit with a special ol.tilegrid but with no success, see here:
https://jsfiddle.net/yvecai/owwc5bo8/8/
The output I aim for is on the right map.
There is no need to create a special tile grid or to apply any canvas scaling. All you need to do is set the tilePixelRatio of the source properly, which would be 1.5 in your case:
source: new ol.source.XYZ({
url: "http://www5.opensnowmap.org/base_snow_map_high_dpi/{z}/{x}/{y}.png?debug",
attributions: [/* ... */],
tilePixelRatio: 1.5
})
Also note that your expectation of the result is wrong. I updated your fiddle to compare the standard 256px tiles (on the right) with the hidpi 384px tiles (on the left). If you are viewing the fiddle on a hidpi display, you'll notice the difference. https://jsfiddle.net/owwc5bo8/9/
To summarize:
If you want to display high-dpi tiles on a mobile device with good sharpness, use tilePixelRatio :
https://jsfiddle.net/owwc5bo8/9/
If you want to display tiles with a size differnet than 256x256, create a proper ol.tilegrid, and a proper ol.view:
https://jsfiddle.net/owwc5bo8/12/
var extent = ol.proj.get('EPSG:3857').getExtent();
var tileSizePixels = 384;
var tileSizeMtrs = ol.extent.getWidth(extent) / 384;
var resolutions = [];
for (var i = -1; i <= 20; i++) {
resolutions[i] = tileSizeMtrs / (Math.pow(2, i));
}
var tileGrid = new ol.tilegrid.TileGrid({
extent: extent,
resolutions: resolutions,
tileSize: [384, 384]
});
var center = ol.proj.fromLonLat(
ol.proj.toLonLat([2, 49], 'EPSG:4326'),
'EPSG:3857');
var zoom = 5;
var view1 = new ol.View({
center: center,
zoom: zoom,
maxResolution: 40075016.68557849 / 384
});

Hot can I get Circle geometry when using ol.interaction.Draw?

I'm drawing a Circle using ol.interaction.Draw.
this.draw = new ol.interaction.Draw({
source: this.vectorSource,
type: 'Circle'
});
this.draw.on('drawend', function( evt ){
// evt.feature have no geometry !!!
}
But How can I get the Circle geometry or anything that tell me how to draw same circle again?
OL3.0 you can create/edit Features, but not Circles... http://openlayers.org/en/v3.0.0/examples/draw-and-modify-features.html
OL3.5 you can create circles, but you can't edit .... http://openlayers.org/en/v3.5.0/examples/draw-features.html
OL_Latest you can't edit circles too: http://openlayers.org/en/latest/examples/draw-and-modify-features.html
I would be happy if I could reproduce the same circle later.
To get the center and radius of the circle feature drawn use the below code
this.draw.on('drawend', function( evt ){
var geometry = evt.feature.getGeometry();
var radius = geometry.getRadius();
var center = geometry.getCenter();
// .... your code
});
Since a circle is drawn the Geometry type would be of type ol.geom.Circle.
Using this Geometry object you can get the radius and center using getRadius() and getCenter() method. For more information about ol.geom.Circle go through this link.
http://openlayers.org/en/latest/apidoc/ol.geom.Circle.html
Almost there!
I'm now able to reproduce the "circle" by adding a geometry function to the draw intaraction.
var geometryFunction = ol.interaction.Draw.createRegularPolygon(90);
this.draw = new ol.interaction.Draw({
source: this.vectorSource,
type: 'Circle',
geometryFunction: geometryFunction,
});
this will generate geometries to the feature so I can store it and load it again later, but it is not a real circle, it is just composed by a 90 points polygon. so I can't edit it as a circle.
Some idea to get the radius of this geometry?
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-4804935.286197088,-2605909.2147242804],[-4804836.58537814,-2605831.0293000625],[-4804743.578928918,-2605746.149310523],[-4804656.719967494,-2605654.988282439],[-4804576.431661996,-2605557.9903431754],[-4804503.105168979,-2605455.6280569425],[-4804437.097727745,-2605348.4001225093],[-4804378.730919913,-2605236.8289435934],[-4804328.289102704,-2605121.458083764],[-4804286.018023582,-2605002.849618253],[-4804252.123622994,-2604881.581395582],[-4804226.771031053,-2604758.244222341],[-4804210.083763039,-2604633.4389848416],[-4804202.143117644,-2604507.773721655],[-4804202.987780893,-2604381.8606613115],[-4804212.613637674,-2604256.3132395847],[-4804230.973791778,-2604131.7431108924],[-4804257.978794381,-2604008.757168381],[-4804293.497079825,-2603887.954587199],[-4804337.355606593,-2603769.923905379],[-4804389.340700352,-2603655.2401565393],[-4804449.199094949,-2603544.462068375],[-4804516.639166305,-2603438.1293405974],[-4804591.332353169,-2603336.7600155673],[-4804672.914757844,-2603240.8479544464],[-4804760.988919055,-2603150.860431155],[-4804855.125748342,-2603067.2358558625],[-4804954.866620535,-2602990.3816390927],[-4805059.72560813,-2602920.672206864],[-4805169.19184868,-2602858.4471765244],[-4805282.732033666,-2602804.009702166],[-4805399.793006718,-2602757.6249976917],[-4805519.804458544,-2602719.5190447196],[-4805642.1817054115,-2602689.8774916218],[-4805766.328537677,-2602668.8447490656],[-4805891.640124453,-2602656.5232864586],[-4806017.505960285,-2602652.973132728],[-4806143.312839472,-2602658.211583866],[-4806268.447843544,-2602672.213118665],[-4806392.301327342,-2602694.909523055],[-4806514.269889141,-2602726.1902224356],[-4806633.759310369,-2602765.902820383],[-4806750.187450577,-2602813.8538411125],[-4806862.987083566,-2602869.809672071],[-4806971.608660867,-2602933.4977020747],[-4807075.522989073,-2603004.607649443],[-4807174.22380802,-2603082.793073661],[-4807267.230257243,-2603167.6730632004],[-4807354.089218667,-2603258.834091285],[-4807434.3775241645,-2603355.8320305482],[-4807507.704017182,-2603458.194316781],[-4807573.711458416,-2603565.4222512143],[-4807632.078266247,-2603676.99343013],[-4807682.520083456,-2603792.3642899594],[-4807724.791162578,-2603910.972755471],[-4807758.685563167,-2604032.240978142],[-4807784.038155108,-2604155.5781513825],[-4807800.725423122,-2604280.383388882],[-4807808.666068517,-2604406.0486520687],[-4807807.821405267,-2604531.961712412],[-4807798.195548487,-2604657.509134139],[-4807779.8353943825,-2604782.0792628312],[-4807752.83039178,-2604905.0652053426],[-4807717.312106336,-2605025.867786525],[-4807673.453579567,-2605143.8984683445],[-4807621.468485809,-2605258.5822171844],[-4807561.610091211,-2605369.3603053484],[-4807494.170019856,-2605475.693033126],[-4807419.4768329915,-2605577.0623581563],[-4807337.8944283165,-2605672.974419277],[-4807249.8202671055,-2605762.9619425684],[-4807155.683437819,-2605846.586517861],[-4807055.9425656255,-2605923.440734631],[-4806951.083578031,-2605993.1501668594],[-4806841.61733748,-2606055.375197199],[-4806728.077152494,-2606109.8126715575],[-4806611.016179442,-2606156.197376032],[-4806491.004727617,-2606194.303329004],[-4806368.627480749,-2606223.944882102],[-4806244.480648483,-2606244.977624658],[-4806119.169061707,-2606257.299087265],[-4805993.303225876,-2606260.8492409955],[-4805867.496346689,-2606255.610789858],[-4805742.361342616,-2606241.6092550587],[-4805618.507858818,-2606218.9128506687],[-4805496.539297019,-2606187.632151288],[-4805377.049875791,-2606147.9195533404],[-4805260.621735584,-2606099.968532611],[-4805147.822102594,-2606044.0127016525],[-4805039.200525293,-2605980.324671649],[-4804935.286197088,-2605909.2147242804]]]},"properties": null}
The Polygon's centroid can be the circle center I think.... the distance of this centroid to any point in the border can be the radius.... I don't know how to get this using JS.
To take the center:
this.draw.on('drawend', function( evt ){
var aa = evt.feature.getGeometry().getExtent();
var oo = ol.extent.getCenter(aa);
console.log( oo );
});
Now I must find a way to take the radius.

Resources