OpenStreetMap add tag on map - javascript

I am working on a webapp with openstreetmap and OpenLayers. What I have done is using JavaScript rendering a line on map with some given points, like the screen capture below:
Now what I would like to do is to have a tag displayed above the line, something like below:
I am not sure how I can achieve this through JavaScript, and I couldn't find any useful reference document. Anyone have done this before please share some experience. Thank you!

Try creating opaque (or zero-width) points with styled labels. Here's my proposition based on Labeled Features Example. (I don't have possibility to test it right now, so watch out).
First: Create new vector layer (as styling settings might interfere with your current layer with line) styled with invisible points and some parameters:
var vectorLayer = new OpenLayers.Layer.Vector("Labeled points", {
styleMap: new OpenLayers.StyleMap({'default':{
// should be invisible, if not, set opaque
strokeWidth: 0,
pointRadius: 0,
// label may have \n linebreaks
label : "${label}",
fontColor: "${fontColor}",
fontSize: "12px",
fontFamily: "Courier New, monospace",
fontWeight: "bold",
labelAlign: "${align}",
labelXOffset: "${xOffset}",
labelYOffset: "${yOffset}",
labelOutlineColor: "white",
labelOutlineWidth: 3
}})
});
Second: Here's how you create feature with given parameters (the ${parameter}'s):
var newLabeledPoint = new OpenLayers.Geometry.Point(-101.04, 35.68);
var newLabeledFeature = new OpenLayers.Feature.Vector(newLabeledPoint );
newLabeledFeature .attributes = {
label: "Line 1",
fontColor: 'blue',
align: "cm",
// positive value moves the label to the right
xOffset: 50,
// negative value moves the label down
yOffset: -15
};
and finally: vectorLayer.addFeatures([newLabeledFeature]);.

Related

Destroy ChartJS rendered with Symfony

i have a little problem with my Symfony/ChartJS Application.
So if i create a Chart with JS like var myChart = new Chart.. and so on, i can easily destroy the Chart with myChart.destory(); because i can address the ChartObject.
My Problem:
The first Chart i Render with my SymfonyController. So i render the chart in my Controller with
return $this->render('category.html.twig', [
'chart' => $chart]);
In Twig i assign an ID to the Chart {{ render_chart(chart, {'id': 'my-chart'}) }}.
But i dont really know how i can adress the whole Chartobject in Js. So how i can destroy the Chart i created with my Symfonycontroller? Anyone have a suggestion?
Thank you in advance!
//EDIT
The normal way with const chart = Chart.getChart('my-chart'); don't work either. Here is my Canvas-ConsoleLog for more information:
<canvas data-controller="symfony--ux-chartjs--chart" data-symfony--ux-chartjs--chart-view-value="(i deleted the values for better legibility)" id="my-chart" style="display: block; box-sizing: border-box; height: 407.778px; width: 816.667px;" width="735" height="367"></canvas>
The strange thing:
when i try the log: console.log(document.getElementById("my-chart").getContext("2d");); it shows:
CanvasRenderingContext2D {canvas: canvas#my-chart, globalAlpha: 1, globalCompositeOperation: 'source-over', filter: 'none', imageSmoothingEnabled: true, …}
canvas: canvas#my-chart
direction: "ltr"
fillStyle: "#000000"
filter: "none"
font: "12px \"Helvetica Neue\", Helvetica, Arial, sans-serif"
globalAlpha: 1
globalCompositeOperation: "source-over"
imageSmoothingEnabled: true
imageSmoothingQuality: "low"
lineCap: "butt"
lineDashOffset: 0
lineJoin: "miter"
lineWidth: 1
miterLimit: 10
shadowBlur: 0
shadowColor: "rgba(0, 0, 0, 0)"
shadowOffsetX: 0
shadowOffsetY: 0
strokeStyle: "#000000"
textAlign: "start"
textBaseline: "alphabetic"
[[Prototype]]: CanvasRenderingContext2D
so the log shows that the chart is recognized...
symfny-ux controller creates its own instance of ChartJS inside an appropriate stimulus-controller, and this variable isn't directly accessible. But, symfony/ux developers are smart, and they created events (e.g. chartjs:connect) which you can listen to (link)
If you do so, you will end up with a JS native CustomEvent object and in the details property of this event you will find passed chart instance previously created in symfiny-ux-controller for chart.js
So in theory you would be able just to
document.addEventListener("chartjs:connect",(chartEv) => {
console.log(chartEv.details); // chartEv.details.chart.destroy()
});
I do recommend you to watch Ryan's tutorial on Symfony UX, especially "Extending a UX Controller" chapter where he tries to work with previously created instance of new Chart() like in your case.
You can use the getChart api method chart.js exposes on itself.
So when you know the ID of your canvas you can use this js to get the chart object:
const chart = Chart.getChart('my-chart');

fabricjs how to double-click a picture(or rect) to edit text and name it

How to combine images and text with fabricjs and edit text when double clicking the combined object,
Still combine after editing text,
Please tell me how to edit text when rendering image and text combinations
const canvas = new fabric.Canvas("canvas");
// a rect
const rect = new fabric.Rect({
stroke: "#000",
strokeWidth: 1,
fill: "#ccc",
left: 170,
top: 80,
width: 50,
height: 50
});
// a text for describe rect
const text = new fabric.IText("describe rect", {
fontFamily: "Comic Sans",
fontSize: 14,
stroke: "#000",
strokeWidth: 1,
fill: "#000",
left: 170,
top: 80
});
// 1 Combine the above two things but can not edit text
// 2 use LabeledRect also
Based on your requirements, you can
Create a subclass of Image class (or whatever object that you need to attach a label to)
Make it create an instance of IText or Textbox and save an internal reference to it
By hooking into the event handlers on the image (e.g. double click), manually manage the events of the text. Make sure to keep the position and dimensions of the text updated when the image is modified.
Take a look at this answer: Resize Fabric Rect without resizing Textbox. It does something very similar to what you've described.

How to blur a closed path element in Paper.js?

Is it possible to blur a closed path element that has a fill using paperjs? Would I have to create an SVG feGaussianBlur primitive for each path that I wanted to blur? How would that affect performance if there were a few hundred path elements on the canvas? I thought about making use of the shadowBlur with some color magic, where the shadow would match the selected path, but it's not quite the same effect. I do not want to use raster.
A trick could be to use the shadowBlur property of the item.
The only thing is that the shadow is only drawn if the item has a fill.
And we don't want the fill to be displayed but only the shadow.
So we could make clever usage of blend modes to hide the fill but not the shadow.
Here's a simple sketch demonstrating this.
new Path.Circle({
center: view.center,
radius: 50,
shadowColor: 'black',
shadowBlur: 20,
selected: true,
// set a fill color to make sure that the shadow is displayed
fillColor: 'white',
// use blendmode to hide the fill and only see the shadow
blendMode: 'multiply',
});
edit
This technique indeed seems to reach a limit when trying to stack items on top of each other.
We can prevent the multiply blend mode from "bleeding out" by wrapping the item into a group which has a blend mode of source-over. This has the effect to scope the children blend modes.
But there is still a trick to find to compose the items together.
Here's a sketch demonstrating where I stopped my investigation.
I'm quite sure that you can find a solution following this track.
function drawBlurryCircle(center, radius, blurAmount, color) {
const circle = new Path.Circle({
center,
radius,
shadowColor: color,
shadowBlur: blurAmount,
// set a fill color to make sure that the shadow is displayed
fillColor: 'white',
// use blendmode to hide the fill and only see the shadow
blendMode: 'multiply'
});
const blurPlaceholder = circle
.clone()
.set({ shadowColor: null, fillColor: null })
.scale((circle.bounds.width + (blurAmount * 2)) / circle.bounds.width);
return new Group({
children: [circle, blurPlaceholder],
blendMode: 'source-over'
});
}
drawBlurryCircle(view.center, 50, 20, 'red');
drawBlurryCircle(view.center + 30, 40, 30, 'lime');
drawBlurryCircle(view.center + 60, 30, 30, 'blue');

OpenLayers 3 - Display text on map, text size relative to zoom

I'm currently working on OL3 and trying to display a text on my map. No problem with that, I'm using the text property of ol.style to display it.
My problem is the following: this text has always the same size. ie: if I'm unzooming max or zooming max, it's still the same size on the screen. I want it to grow while zooming, and shrink while unzooming to keep the text's size relative to the ground.
I drew the expected result in the following image:
Expected result
Is anyone having an idea? Maybe without using a ol.style text?
The way to go is a style function. See demo fiddle.
var style = function () {
var zoom = map.getView().getZoom();
var font_size = zoom * 10; // arbitrary value
return [
new ol.style.Style({
text: new ol.style.Text({
font: font_size + 'px Calibri,sans-serif',
fill: new ol.style.Fill({ color: '#000' }),
stroke: new ol.style.Stroke({
color: '#fff', width: 2
}),
text: 'Some text!'
})
})
];
};

How do I 'forward' (mouse) events to a node/shape on a lower Zindex in kineticjs

I'm working with kineticjs where I have a stage with one layer in it. This layer has multiple shapes on it who react on a mouseover event (they show a tooltip)
besides these shapes I also have some text nodes on a higher ZIndex (so they are above the shapes).
This all works fine with one exception, when a text is on top of a shape, the shape doesn't show a tooltip I assume it's because the textnode uses the event, rather than 'forward' it, eventhough I haven't bound anything to the text node.
How can I propagate events to nodes on a lower ZIndex?
Personally I'd create a separate layer to put anything you dont want to register mouse events on and when you create the layer set its listening property to false.
You could also set the this property for the individual elements.
Here's an example using layers...
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var normalLayer = new Kinetic.Layer();
var textLayer = new Kinetic.Layer({listening :false});
var rect = new Kinetic.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4
});
rect.on('click', function(evt) {
this.setFill('blue');
normalLayer.draw();
});
normalLayer.add(rect);
var simpleText = new Kinetic.Text({
x: 40,
y: 40,
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
textFill: 'yellow'
});
textLayer.add(simpleText);
stage.add(normalLayer);
stage.add(textLayer);
http://jsfiddle.net/MT435/
Remember the listening property can be set for individual elements aswell.

Categories