Set feature before selected element in OpenLayers - javascript

I have a some features that must be always in front of any other feature, the problem is that when I draw new feature or select current one it overlaps all other features.
Is there a way I can set some features or a layer to be always on top of everything ?

I suspect you are not specifying zIndex in the selected style for the interaction. In this code sample for hover on features in certain layers I set the selected style to be the same as the normal unselected style for the layer and only change the cursor
var interStyle;
function interFilter(feature, layer) {
for (var i = 0; i < layerTypes.length; i++) {
if (layer && layer.get("jsonName") && layer.get("jsonName").slice(-7) == layerTypes[i] + ".json") {
interStyle = layer.getStyle();
return true;
}
}
return false;
}
var interHover = new ol.interaction.Select({ condition: ol.events.condition.pointerMove,
style: function(feature) { return interStyle; },
filter: interFilter });
interHover.on( "select",
function(event) { rowMap.getViewport().style.cursor = event.selected.length > 0 ? "pointer" : "";
// optional callback to inform the calling page the mouse is over/off a feature
if (options.setMouseOver) { options.setMouseOver(event.selected.length > 0); } }
);
The purple line remains below the green line at all times:
However if I comment out the style setting
var interHover = new ol.interaction.Select({ condition: ol.events.condition.pointerMove,
// style: function(feature) { return interStyle; },
filter: interFilter });
When hovering over the purple line it takes OpenLayers default blue selected style and appears above the green line

Related

Angular - Can Leaflet measurement tool mute a layer's interactivity?

I'm using Leaflet.Measure for measuring areas and distances and I also have several layers on a map. When you click on a layer, the onclick function is being fired and a popup appears. When I start to measure a new area and click on a map layer, the popup appears again and I cannot measure area further. Is there any possibility to mute layer's interactivity, while I'm using measurement tool?
I tired to import my Angular service class into a measuring plugin to change layer's onclick behaviour, but it didn't work.
Here's a part of Leaflet.Measure plugin code, which I have modified:
import { MapService} from './app/services/map.service';
L.MeasureAction = L.Handler.extend({
_onMouseClick: function (event) {
MapService.enablePopupOpen = false; // if it's set to false, then popup won't open
//default plugin code
var latlng = event.latlng || this._map.mouseEventToLatLng(event);
if (this._lastPoint && latlng.equals(this._lastPoint)) {
return;
}
if (this._trail.points.length > 0) {
var points = this._trail.points;
points.push(latlng);
var length = points.length;
this._totalDistance += this._getDistance(points[length - 2], points[length - 1]);
this._addMeasurePoint(latlng);
//this._addMarker(latlng);
if (this.options.model !== "area") {
this._addLable(latlng, this._getDistanceString(this._totalDistance), "leaflet-measure-lable");
}
} else {
this._totalDistance = 0;
this._addMeasurePoint(latlng);
this._addMarker(latlng);
if (this.options.model !== "area") {
this._addLable(latlng, L.Measure.start, "leaflet-measure-lable");
}
this._trail.points.push(latlng);
}
this._lastPoint = latlng;
this._startMove = false;
},
...
})

OpenLayers toggle feature visibility (Show/hide)

I am trying to show or hide features on click.
I have many points with different colors, I am trying to change opacity to 0/1.
What I managed to do is set 2 different feature styles and use setStyle on click.
I can hide a feature but when I try to unhide it all features become the same image I use instead of going to colors they were before hiding. I'll try to explain better with picture examples and some code snippets. Image 1 shows features on map load, Image 2 shows features when toggled to Hide, Image 3 shows features when toggled to Show (I don't want features to be styled like that, I want to features be styled as they were in Image 1)
This is the code snippet:
//visible style
var visibleStyleIcon = {
Point: [
new ol.style.Style({
image: new ol.style.Icon({
src: "https://openlayers.org/en/latest/examples/data/dot.png",
opacity: 1,
}),
}),
],
};
// invisible Style Icon Opacity
var invisibleStyleIcon = {
Point: [
new ol.style.Style({
image: new ol.style.Icon({
src: "https://openlayers.org/en/latest/examples/data/dot.png", //still need something even if it's invisible
opacity: 0,
}),
}),
],
};
forEachFeatureInExtent(extent, function (feature) {
if (
Object.values(Object.values(feature.get("info"))[0][2])[1] === t
) {
if (e.target.className === "menu-selector2")
feature.setStyle(
invisibleStyleIcon[feature.getGeometry().getType()]
);
if (e.target.className === "menu-selector")
feature.setStyle(
visibleStyleIcon[feature.getGeometry().getType()]
);
}
});
So is there a way to just set opacity for feature to 0 or 1?
I tried this with no success.
var style = feature.getStyle();
var color = style.getFill().getColor();
var colorArray = ol.color.asArray(color).slice();
colorArray[3] = 1;
style.getFill().setColor(colorArray);
feature.setStyle(style);
selectedLayer.redraw();
I found this also:
feature.getStyle().getImage().setOpacity(0);
But that function shows/hides all points with same Style, not just the selected one. For example, if I want to hide 1 feature and its a grey circle, it will hide all grey circles in extent.
Layer setStyle method will iterate through all features of that layer.
you should have a function like this example, and every time that you want style features based on a specific condition (it can be feature id or any other property) you can call this function and pass the layer and featureId and style your features accordingly.
function setLayerStyle(layer, featureId) {
layer.setStyle(feature => {
if (feature.getProperties().id === featureId) {
return style a
} else {
return style b
}
})
}

Trigger mouseover event on a Raphael map

I am using the Raphael plugin for the first time and so far I managed to create a map of Germany and to outline all different regions inside. I found out how to bind mouse-over effects, so when I hover over a region its color changes. Everything looks fine until the moment when I want to trigger the same mouse-over effect from outside the map. There is a list of links to all the regions and each link should color its respective geographical position (path) on the map when hovered. The problem is that I don't know how to trigger the mouse-over effect from outside.
This is the reference guide I used for my code: Clickable France regions map
This is my map initialization:
var regions = [];
var style = {
fill: "#f2f2f2",
stroke: "#aaaaaa",
"stroke-width": 1,
"stroke-linejoin": "round",
cursor: "pointer",
class: "svgclass"
};
var animationSpeed = 500;
var hoverStyle = {
fill: "#dddddd"
}
var map = Raphael("svggroup", "100%", "auto");
map.setViewBox(0, 0, 585.5141, 792.66785, true);
// declaration of all regions (states)
....
var bayern = map.path("M266.49486,..,483.2201999999994Z");
bayern.attr(style).data({ 'href': '/bayern', 'id': 'bayern', 'name': 'Bayern' }).node.setAttribute('data-id', 'bayern');
regions.push(bayern);
This is where my "normal" mouse effects take place:
for (var regionName in regions) {
(function (region) {
region[0].addEventListener("mouseover", function () {
if (region.data('href')) {
region.animate(hoverStyle, animationSpeed);
}
}, true);
region[0].addEventListener("mouseout", function () {
if (region.data('href')) {
region.animate(style, animationSpeed);
}
}, true);
region[0].addEventListener("click", function () {
var url = region.data('href');
if (url){
location.href = url;
}
}, true);
})(regions[regionName]);
}
I have a menu with links and I want to bind each of them to the respective region, so that I can apply the animations, too.
$("ul.menu__navigation li a").on("mouseenter", function (e) {
// this function displays my pop-ups
showLandName($(this).data("id"));
// $(this).data("id") returns the correct ID of the region
});
I would appreciate any ideas! Thanks in advance!
I figured out a way to do it. It surely isn't the most optimal one, especially in terms of performance, but at least it gave me the desired output. I would still value a better answer, but as of right now a new loop inside the mouse-over event does the job.
$("ul.menu__navigation li a").on("mouseenter", function (e) {
// this function displays my pop-ups
showLandName($(this).data("id"));
// animate only the one hovered on the link list
var test = '/' + $(this).data("id");
for (var regionName in regions) {
(function (region) {
if (region.data('href') === test) {
region.animate(hoverStyle, animationSpeed);
}
})(regions[regionName]);
}
});

How to create custom legend in ChartJS

I need to create custom legend for my donut chart using ChartJS library.
I have created donut with default legend provided by ChartJS but I need some modification.
I would like to have value above the car name. Also I don't like sticky legend I want to have it separate from donut so I can change the style for fonts, boxes (next to the text "Audi" for example)
I know there is some Legend generator but I'm not sure how to use it with VueJS - because I'm using VueJS as a framework
This is how my legend looks like now - http://imgur.com/a/NPUoi
My code:
From Vue component where I import a donut component:
<div class="col-md-6">
<div class="chart-box">
<p class="chart-title">Cars</p>
<donut-message id="chart-parent"></donut-message>
</div>
</div>
Javascript:
import { Doughnut } from 'vue-chartjs'
export default Doughnut.extend({
ready () {
Chart.defaults.global.tooltips.enabled = false;
Chart.defaults.global.legend.display = false;
this.render({
labels: ['Audi','BMW','Ford','Opel'],
datasets: [
{
label: 'Cars',
backgroundColor: ['#35d89b','#4676ea','#fba545','#e6ebfd'],
data: [40, 30, 20, 10]
}
]
},
{
responsive: true,
cutoutPercentage: 75,
legend: {
display: true,
position: "right",
fullWidth: true,
labels: {
boxWidth: 10,
fontSize: 14
}
},
animation: {
animateScale: true
}
})
}
});
I'm having the same problem trying to understand the documentation and this link might clarify the process of customize the legends:
https://codepen.io/michiel-nuovo/pen/RRaRRv
The trick is to track a callback to build your own HTML structure and return this new structure to ChartJS.
Inside the options object:
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
for (var i = 0; i < chart.data.datasets[0].data.length; i++) {
text.push('<li><span style="background-color:' +
chart.data.datasets[0].backgroundColor[i] + '">');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push('</span></li>');
}
text.push('</ul>');
return text.join("");
}
Second, you need a container to insert the new html and using the method myChart.generateLegend() to get the customized html:
$("#your-legend-container").html(myChart.generateLegend());
After that, if you need, track down the events:
$("#your-legend-container").on('click', "li", function() {
myChart.data.datasets[0].data[$(this).index()] += 50;
myChart.update();
console.log('legend: ' + data.datasets[0].data[$(this).index()]);
});
$('#myChart').on('click', function(evt) {
var activePoints = myChart.getElementsAtEvent(evt);
var firstPoint = activePoints[0];
if (firstPoint !== undefined) {
console.log('canvas: ' +
data.datasets[firstPoint._datasetIndex].data[firstPoint._index]);
}
else {
myChart.data.labels.push("New");
myChart.data.datasets[0].data.push(100);
myChart.data.datasets[0].backgroundColor.push("red");
myChart.options.animation.animateRotate = false;
myChart.options.animation.animateScale = false;
myChart.update();
$("#your-legend-container").html(myChart.generateLegend());
}
}
Another solution that I found, if you don't need to change the HTMl structure inside the legend, you can just insert the same HTML in your legend container and customize it by CSS, check this another link:
http://jsfiddle.net/vrwjfg9z/
Hope it works for you.
You can extract the legend markup.
data () {
return {
legendMarkup: ''
}
},
ready () {
this.legendMarkup = this._chart.generateLegend()
}
And in your template you can output it.
<div class="legend" ref="legend" v-html="legendMarkup"></div>
this._chart is the internal chartjs instance in vue-chartjs. So you can call all chartjs methods which are not exposed by vue-chartjs api over it.
However you can also use the legend generator. The usage is the same in vue. You can pass in the options, use callbacks etc.
please check this documentation
.
Legend Configuration
The chart legend displays data about the datasets that area appearing on the chart.
Configuration options
Position of the legend. Options are:
'top'
'left'
'bottom'
'right'
Legend Item Interface
Items passed to the legend onClick function are the ones returned from labels.generateLabels. These items must implement the following interface.
{
// Label that will be displayed
text: String,
// Fill style of the legend box
fillStyle: Color,
// If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect
hidden: Boolean,
// For box border. See https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap
lineCap: String,
// For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
lineDash: Array[Number],
// For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
lineDashOffset: Number,
// For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
lineJoin: String,
// Width of box border
lineWidth: Number,
// Stroke style of the legend box
strokeStyle: Color
// Point style of the legend box (only used if usePointStyle is true)
pointStyle: String
}
Example
The following example will create a chart with the legend enabled and turn all of the text red in color.
var chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
legend: {
display: true,
labels: {
fontColor: 'rgb(255, 99, 132)'
}
}
}
});
Custom On Click Actions
It can be common to want to trigger different behaviour when clicking an item in the legend. This can be easily achieved using a callback in the config object.
The default legend click handler is:
function(e, legendItem) {
var index = legendItem.datasetIndex;
var ci = this.chart;
var meta = ci.getDatasetMeta(index);
// See controller.isDatasetVisible comment
meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
// We hid a dataset ... rerender the chart
ci.update();
}
Lets say we wanted instead to link the display of the first two datasets. We could change the click handler accordingly.
var defaultLegendClickHandler = Chart.defaults.global.legend.onClick;
var newLegendClickHandler = function (e, legendItem) {
var index = legendItem.datasetIndex;
if (index > 1) {
// Do the original logic
defaultLegendClickHandler(e, legendItem);
} else {
let ci = this.chart;
[ci.getDatasetMeta(0),
ci.getDatasetMeta(1)].forEach(function(meta) {
meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
});
ci.update();
}
};
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
legend: {
}
}
});
Now when you click the legend in this chart, the visibility of the first two datasets will be linked together.
HTML Legends
Sometimes you need a very complex legend. In these cases, it makes sense to generate an HTML legend. Charts provide a generateLegend() method on their prototype that returns an HTML string for the legend.
To configure how this legend is generated, you can change the legendCallback config property.
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
legendCallback: function(chart) {
// Return the HTML string here.
}
}
});

Chart.js : Multiple colors for tooltipFontColor

I know that the font color for the tooltip labels can be changed with the toolTipFontColor option, but that forces me to give one colour for all labels. Is there a way to set the colour for a label to, say, white, but then change it to red if a condition holds. I was thinking of something using the customTooltips option but any solution is welcome.
Example of what I'd like to do:
if (difference[i] >= 0) {
tooltipFontColor: "#FFF"
} else {
tooltipFontColor: "#F00"
}
The trick is that the difference array can have some values above zero and some below. I want Chart.js to somehow understand that and follow through with the colours.
Thanks!
Different Colors for Toooltip Based on Value
Preview
Script
new Chart(ctx).Line(data, {
// take over the tooltip binding
tooltipEvents: [],
onAnimationComplete: function () {
// copy of chart.js binding except that ...
Chart.helpers.bindEvents(this, ["mousemove", "touchstart", "touchmove"], function (evt) {
var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
this.eachPoints(function (point) {
point.restore(['fillColor', 'strokeColor']);
});
Chart.helpers.each(activePoints, function (activePoint) {
activePoint.fillColor = activePoint.highlightFill;
activePoint.strokeColor = activePoint.highlightStroke;
});
// ... we set the font color before the tooltip is shown
if (activePoints.length !== 0) {
if (activePoints[0].value >= 0)
this.options.tooltipFontColor = "#FFF";
else
this.options.tooltipFontColor = "#F00";
}
this.showTooltip(activePoints);
});
}
});
Fiddle - http://jsfiddle.net/uajz17yp/

Categories

Resources