cant set feature (marker/icon) from forEachTileCoord coords, openlayers - maps

new to openlayers, i am currently trying to grab all tiles at certain zoom level within the view extent and add a feature/icon to each tile location, i can grab tile coords using
src.getSource().getTileGrid().forEachTileCoord
problem is it returns Z,X,Y tile coords (ie [16, 31689, 20858]) and i cant find anyway to either set icons location with these coords or convert them to lon/lat (eg 'EPSG:4326'[-5.928909184411161, 54.5921176536682]
iv tried
ol.proj.toLonLat
but it returns 0.00014373044545912343, 0.284665959233962, 20858.
as im new i may have something fundamentally wrong with my setup, or im completely missing something,thanks

To get actual coordinates you can use getTileCoordExtent / getCenter. This will get you the center coordinates for each tile.
By default the coordinate format is EPSG:3857 and no conversion should be needed to add a Feature.
const zDirection = 0;
const tileLayer = new ol.layer.Tile({
source: new ol.source.TileDebug({
zDirection: zDirection,
})
});
const style = new ol.style.Style({
image: new ol.style.Circle({
radius: 70,
fill: new ol.style.Fill({color: 'rgba(255, 0, 0, .2)'}),
}),
});
const layer = new ol.layer.Vector({
style: style,
});
const map = new ol.Map({
target: 'map',
view: new ol.View({
center: [0, 5000000],
zoom: 2,
}),
layers: [tileLayer, layer],
});
map.on('moveend', function () {
const view = map.getView();
const tileGrid = tileLayer.getSource().getTileGrid();
const extent = view.calculateExtent();
const zoom = tileGrid.getZForResolution(view.getResolution(), zDirection);
const features = [];
tileGrid.forEachTileCoord(extent, zoom, function (tileCoord) {
const center = ol.extent.getCenter(tileGrid.getTileCoordExtent(tileCoord));
features.push(new ol.Feature(new ol.geom.Point(center)));
});
layer.setSource(new ol.source.Vector({features: features}));
});
html, body {
padding: 0;
margin: 0;
}
body {
display: flex;
justify-content: stretch;
height: 100vh;
width: 100vw;
}
#map {
flex: 1;
}
<head>
<link href="https://cdn.jsdelivr.net/gh/openlayers/openlayers#v6.14.1/src/ol/ol.css" rel="stylesheet"/>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.14.1/build/ol.js"></script>
</body>

Related

custom legend hide() does not remove data labels

I am building a project using React with a doughnut and bar chart. Working with Chart.js 3.xx.
I am trying to make my custom legend functional. I want to make data fractions disappear when the user clicks my legend items - like in the native legend, and optimally also remove the data and make the chart present it's updated data after removal.
I also use data labels to present percentage of the data on the fractions.
import ChartDataLabels from 'chartjs-plugin-datalabels';
I came across this topic: ChartJS - Show/hide data individually instead of entire dataset on bar/line charts
and used this suggested code there:
function chartOnClick(evt) {
let chart = evt.chart
const points = chart.getElementsAtEventForMode(evt, 'nearest', {}, true);
if (points.length) {
const firstPoint = points[0];
//var label = myChart.data.labels[firstPoint.index];
//var value = myChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];
let datasetIndex = firstPoint.datasetIndex, index = firstPoint.index;
if (firstPoint.element.hidden != true) {
chart.hide(datasetIndex, index);
} else {
chart.show(datasetIndex, index);
}
}
}
options: { // chart options
onClick: chartOnClick
}
It almost works, but the hide() method doesn't remove the fraction's percentage data label when activated, whereas when clicking the native legend it does remove it entirely.
I tried looking in the plugin's docs but didn't manage to find how to remove a single label.
How can I achieve what I am looking for?
EDIT:
Options Object:
export const doughnutOptsObj = {
onClick: chartOnClick,
responsive: true,
maintainAspectRatio: false,
layout: { padding: { top: 16, bottom: 16 } },
hoverOffset: 32,
plugins: {
legend: {
display: true,
position: 'bottom',
},
datalabels: {
formatter: (value, dnct1) => {
let sum = 0;
let dataArr = dnct1.chart.data.datasets[0].data;
dataArr.map((data) => {
sum += Number(data);
});
let percentage = ((value * 100) / sum).toFixed() + '%';
return percentage;
},
color: ['#fbfcfd'],
font: { weight: 'bold' },
// display: false, <-- this works and makes all of the data labels disappear
},
},
};
It seems that the onClick function is working properly.
I have tried the attached code, leveraging on toggleDataVisibility API, and it's working as requested (codepen: https://codepen.io/stockinail/pen/abKNJqJ):
function chartOnClick(evt) {
let chart = evt.chart
const points = chart.getElementsAtEventForMode(evt, 'nearest', {}, true);
if (points.length) {
const firstPoint = points[0];
chart.toggleDataVisibility(firstPoint.index);
chart.update();
}
}

Filling a chart with a color above a specific Y value

I've been trying to fill a chart with color above certain Y value and I can't do it.
What I tried to do is writing a condition that if the value is >3.5 that area of the chart will be filled with a different color.
splineSeries.fill(function() {
if (this.value > 3.5)
return '#d3f335 0.4'
else
return '#cdf0a7 0.6'})
However, this doesn't work for me as it fills the whole area of the chart which values are >3.5 and not only the area that it's above the line.
This is how that chunk of code is working in my chart.1
If you know how this can be solved I would really appreciate if you help me :)
Thanks!!
It does not seem AnyChart supports this kind of filling out of the box. You can however get creative with gradients:
var cmin = chart.getStat("yScalesMin");
var cmax = splineSeries.getStat('seriesMax');
var cutoff = 3.5;
splineSeries.fill({
angle: 90,
keys: [{
color: '#cdf0a7',
opacity: 0.6,
offset: (cutoff-cmin) / (cmax-cmin)
}, {
color: '#d3f335',
opacity: 0.4,
offset: (cutoff-cmin) / (cmax-cmin)
}],
thickness: 3
});
Here's a working example:
chart = anychart.area();
var series = chart.area(generateRandomData());
var cmin = chart.getStat("yScalesMin");
var cmax = series.getStat('seriesMax');
var cutoff = 3.5;
series.fill({
angle: 90,
keys: [{
color: '#cdf0a7',
opacity: 0.6,
offset: (cutoff-cmin) / (cmax-cmin)
}, {
color: '#d3f335',
opacity: 0.4,
offset: (cutoff-cmin) / (cmax-cmin)
}],
thickness: 3
});
chart.container("chart");
chart.draw();
function generateRandomData() {
var data = [];
for (let i=0; i<16; i++) {
data.push({x:i, value:Math.random() * 7.5});
}
return data;
}
#chart {
height: 400px;
}
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-cartesian.min.js"></script>
<div id="chart"></div>
This should also work with charts with yScalesMin > 0

How to add local image of project folder in canvas?

I am trying to open a project folder image in the canvas without uploading it in a file input. But it is not showing there and I am using react-konva to add the image.
Here is the code where I am trying to upload the image:
const img = new window.Image();
img.src = require('../../../assets/images/71.png');
img.onload = function () {
const img_width = this.width;
console.log('the image', require('../../../assets/images/71.png'));
const img_height = this.height;
const max = img_width > img_height ? 100 : 100;
const ratio = (img_width > img_height ? (img_width / max) : (img_height / max))
setOriginalDim({ width: img_width, height: img_height });
setDimensions({ width: img_width/ratio, height: img_height/ratio });
const theImg = new Konva.Image({
image: this,
x: 0,
y: 0,
width: img_width/ratio,
height: img_height/ratio,
name: 'background_image',
draggable: true
});
if (layerRef.current != null) {
console.log('image', theImg);
layerRef.current.add(theImg);
layerRef.current.draw();
}
};
And my react component is this:
<Stage
width={dimensions.width}
height={dimensions.height}
onMouseDown={handleStageMouseDown}
style={{ backgroundColor: '#fff', boxShadow: '0 0 6px 0 #666' }}
ref={stageRef}
id="certificate_canvas"
>
<Layer
ref={layerRef}
/>
</Stage>
The onload part is not executing also the image is not loading into the canvas. How do I add the project folder image without uploading it
instead of this
img.src = require('../../../assets/images/71.png');
use
img.src = '../../../assets/images/71.png';
you dont need the require function

QuillJS Reload toolbar controls

I am using QuillJS and I need to add some controls to toolbar during runtime. Is there any way to make it from code after whole Quill has been initialized?
This is how I make it now.
quillEditor.getModule('toolbar').addHandler('color', (value) => {
if (value == 'new-color') {
value = prompt('Give me hex color baby!');
// unfortunately this code does not work
let n = toolbar.querySelector('select.ql-color');
n.innerHTML += '<option value="'+value+'"></option>';
}
quillEditor.format('color', value);
console.log("Color handler", value);
});
It looks like you're only adding the new options to the select element which is hidden. The element used in the UI to select colors is a span with the class ql-picker-options.
Check out this snippet
var tools = [
['bold', 'italic', 'underline', 'strike'],
[{'color': ['red', 'blue', 'green', 'new-color']}]
]
var quillEditor = new Quill('#editor-container', {
modules: {
toolbar: tools
},
theme: 'snow'
});
var toolbar = document.querySelector('.ql-toolbar');
quillEditor.getModule('toolbar').addHandler('color', (value) => {
if (value == 'new-color') {
value = prompt('Give me hex color baby!');
// This element is what the user sees
let uiSelect = toolbar.querySelector('.ql-color .ql-picker-options');
// This is a hidden element
let select = toolbar.querySelector('select.ql-color');
uiSelect.innerHTML += '<span class="ql-picker-item ql-primary" data-value="'+value+'" style="background-color: '+value+';"></span>';
select.innerHTML += '<option value="'+value+'"></option>';
}
quillEditor.format('color', value);
});
.ql-color .ql-picker-options [data-value=new-color] {
background: none !important;
width: 90px !important;
height: 20px !important;
}
.ql-color .ql-picker-options [data-value=new-color]:before {
content: 'New Color';
}
<script src="//cdn.quilljs.com/1.3.4/quill.min.js"></script>
<link href="//cdn.quilljs.com/1.0.0/quill.snow.css" rel="stylesheet"/>
<link href="//cdn.quilljs.com/1.3.4/quill.core.css" rel="stylesheet"/>
<div id="editor-container"></div>
Then with insertBefore(), you could keep the "New Color" option at the end.

Marker Clusterer in DevExtreme Mobile

I'm developing an application in DevExtreme Mobile. In application, I use DXMap in this application. How can I use the marker clusterer structure in DevExtreme Mobile App?
You can use Google Maps Marker Clusterer API to create and manage per-zoom-level clusters for a large number of DevExtreme dxMap markers. Here is an example:
 dxMap Marker Clusterer
This example is based on the approach described in the Google Too Many Markers! article
Here is sample code:
$("#dxMap").dxMap({
zoom: 3,
width: "100%",
height: 800,
onReady: function (s) {
var map = s.originalMap;
var markers = [];
for (var i = 0; i < 100; i++) {
var dataPhoto = data.photos[i];
var latLng = new google.maps.LatLng(dataPhoto.latitude, dataPhoto.longitude);
var marker = new google.maps.Marker({
position: latLng
});
markers.push(marker);
}
var markerCluster = new MarkerClusterer(map, markers);
}
});
The kry is to use the google maps api. I did it for my app, here how.
This the html, very simple:
<div data-options="dxView : { name: 'map', title: 'Punti vendita', pane: 'master', secure:true } ">
<div data-bind="dxCommand: { id: 'back', behavior: 'back', type: 'back', visible: false }"></div>
<div data-options="dxContent : { targetPlaceholder: 'content' } ">
<div style="width: 100%; height: 100%;">
<div data-bind="dxMap:options"></div> <!--this for the map-->
<div id="large-indicator" data-bind="dxLoadIndicator: {height: 60,width: 60}" style="display:inline;z-index:99;" />
<div data-bind="dxPopover: {
width: 200,
height: 'auto',
visible: visible,
}">
</div>
</div>
</div>
</div>
When the page loads, the app read the gps coordinates:
function handleViewShown() {
navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
jQuery("#large-indicator").css("display", "none"); //this is just a gif to indicate the user to wait the end of the operation
}
If the gps location is correctly read, I save the coordinates (the center of the map):
function onSuccess(position) {
var lat1 = position.coords.latitude;
var lon1 = position.coords.longitude;
center([lat1, lon1]);
}
And these are the options I set to my dxMap:
options: {
showControls: true,
key: { google: "myGoogleApiKey" },
center: center,
width: "100%",
height: "100%",
zoom: zoom,
provider: "google",
mapType: "satellite",
autoAdjust: false,
onReady: function (s) {
LoadPoints();
var map = s.originalMap;
var markers = [];
for (var i = 0; i < MyPoints().length; i++) {
var data = MyPoints()[i];
var latLng = new google.maps.LatLng(data.location[0], data.location[1]);
var marker = createMarker(latLng, data.title, map, data.idimp);
markers.push(marker);
}
var markerCluster = new MarkerClusterer(map, markers, { imagePath: 'images/m' });
}
},
Where MyPoints is populated calling LoadPoints:
function LoadPoints() {
$.ajax({
type: "POST",
async:false,
contentType: "application/json",
dataType: "json",
url: myApiUrl,
success: function (Response) {
var tempArray = [];
for (var point in Response) {
var location = [Response[p]["latitudine"], Response[p]["longitudine"]];
var title = Response[p]["name"] + " - " + Response[p]["city"];
var temp = { title: title, location: location, tooltip: title, onClick: GoToNavigator, idpoint: Response[p]["id"] };
tempArray.push(temp);
}
MyPoints(tempArray);
},
error: function (Response) {
jQuery("#large-indicator").css("display", "none");
var mex = Response["responseText"];
DevExpress.ui.notify(mex, "error");
}
});
}
Note that in the folder Myproject.Mobile/images I included the images m1.png, m2.png, m3.png, m4.png and m5.png.
You can found them here.

Resources