How to add an marker to leaflet map image overlay by using the image pixel coordinates.
I need to place an marker into map by the image's actual pixel xy coordinates by mentioning the x,y pixel points
Check this Fiddle link for more info.
var osmUrl = 'path/to/custom/image',
h = 709,
w = 709;
var map = L.map('mapid', {
minZoom: 1,
maxZoom: 1,
center: [0, 0],
zoom: 1,
crs: L.CRS.Simple
});
var southWest = map.unproject([0, h], map.getMaxZoom());
var northEast = map.unproject([w, 0], map.getMaxZoom());
var bounds = new L.LatLngBounds(southWest, northEast);
L.imageOverlay(osmUrl, bounds).addTo(map);
map.setMaxBounds(bounds);
// var markerData = [[0,0],[185,362],[277,593],[307,354],[472,472],[473,568],[550,516],[535,370],[230,119]];
var marker = L.marker(map.layerPointToLatLng(map.containerPointToLayerPoint([185, 362])));
marker.addTo(map);
<div id="mapid" style="width:500px;height:400px;></div>
The answer is yes. Your map center was wrong. The axis origin is at the top-left corner.
var map = L.map('mapid', {
minZoom: -3,
maxZoom: 1,
center: [0, -709],
zoom: 1,
crs: L.CRS.Simple
});
Check that fiddle and that other one.
Related
When using Surface, I can pass a grid for X and Y axis to the constructor:
Array<float> z = new float[,]
{
{1, 2, 3 },
{4, 5, 6 }
};
Array<float> x = new float[,]
{
{-0.5f, 0, 0.5f },
};
Array<float> y = new float[,]
{
{-10, -1 },
};
var linSurface = new Surface(z, x, y, colormap: Colormaps.Hsv);
var linContour = new ContourPlot(z, colormap: Colormaps.Hsv);
Can I do something similar (control X and Y axis) for ContourPlot?
ContourPlot currently does not have the option to specify custom coordinates for X or Y values. However, you can use a trick to 'move' the contour plot to custom positions. For it to work, put the ContourPlot into a Group node. Groups allow to translate their content to arbitrary positions. They also allow to stretch the content.
For example, to create an axis range [10...110] for X and [20,220] for Y
(pseudo code, have not tested):
var linContour = new Group(
scale: new Vector3(100, 200, 0),
translate: new Vector3(10,20,1)) {
new ContourPlot(z, colormap: Colormaps.Hsv)
};
Wrong
Right
Ive got a CRS.simple map made from x y coordinates. When inserted with markers, they go as the 1st image shows, but they should go as they are in the 2nd image. How do i get that rotation to happen?
The CRS.Simple coords are in the format [y,x]
You can switch them with following:
var yx = L.latLng;
var xy = function(x, y) {
if (L.Util.isArray(x)) { // When doing xy([x, y]);
return yx(x[1], x[0]);
}
return yx(y, x); // When doing xy(x, y);
};
https://leafletjs.com/examples/crs-simple/crs-simple.html#this-is-not-the-latlng-youre-looking-for
Update
Mirror the coords with adding a minus:
var yx = L.latLng;
var xy = function(x, y) {
if (L.Util.isArray(x)) { // When doing xy([x, y]);
return yx(-x[1], -x[0]);
}
return yx(-y, -x); // When doing xy(x, y);
};
I need this type of gauge chart
How do I create the above guage chart
I have the speedometer gauge working, but it doesn't meet the needs.
Is there a way within the highchart api to use a triangle for the gauge and not the speedometer?
Unfortunately, it is not supported by default. However, you can achieve it wrapping Highcharts.seriesTypes.gauge.prototype.translate method and changing gauge dial element path. Check demo and code posted below.
Wrapper code:
(function(H) {
H.seriesTypes.gauge.prototype.translate = function() {
var series = this,
yAxis = series.yAxis,
options = series.options,
center = yAxis.center,
pInt = H.pInt,
merge = H.merge,
pick = H.pick,
isNumber = H.isNumber;
series.generatePoints();
series.points.forEach(function(point) {
var dialOptions = merge(options.dial, point.dial),
radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) /
200,
baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) /
100,
rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) /
100,
baseWidth = dialOptions.baseWidth || 3,
arrowHeight = dialOptions.arrowHeight || 10,
arrowWidth = dialOptions.arrowWidth || 5,
topWidth = dialOptions.topWidth || 1,
overshoot = options.overshoot,
rotation = yAxis.startAngleRad +
yAxis.translate(point.y, null, null, null, true);
// Handle the wrap and overshoot options
if (isNumber(overshoot)) {
overshoot = overshoot / 180 * Math.PI;
rotation = Math.max(
yAxis.startAngleRad - overshoot,
Math.min(yAxis.endAngleRad + overshoot, rotation)
);
} else if (options.wrap === false) {
rotation = Math.max(
yAxis.startAngleRad,
Math.min(yAxis.endAngleRad, rotation)
);
}
rotation = rotation * 180 / Math.PI;
point.shapeType = 'path';
point.shapeArgs = {
d: dialOptions.path || [
'M', -rearLength, -baseWidth / 2,
'L',
baseLength, -baseWidth / 2,
baseLength, -arrowWidth,
baseLength + arrowHeight, topWidth / 2,
baseLength, arrowWidth,
baseLength, baseWidth / 2, -rearLength, baseWidth / 2,
'z'
],
translateX: center[0],
translateY: center[1],
rotation: rotation
};
// Positions for data label
point.plotX = center[0];
point.plotY = center[1];
});
}
})(Highcharts);
Demo:
https://jsfiddle.net/BlackLabel/07c3dkn4/1/
I wondered if it was possible to make the circle, that appears once hovered across the Google line chart, have a coloured border?
you can use a style column role to change the style of a point.
point {stroke-width: 2; stroke-color: red;}
see following working snippet,
hover a point to see the style...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = new google.visualization.DataTable();
data.addColumn('number', 'x');
data.addColumn('number', 'y');
data.addColumn({role: 'style', type: 'string'});
data.addRows([
[0, 1, 'point {stroke-width: 2; stroke-color: red;}'],
[1, 2, 'point {stroke-width: 2; stroke-color: red;}'],
[2, 3, 'point {stroke-width: 2; stroke-color: red;}'],
[3, 4, 'point {stroke-width: 2; stroke-color: red;}'],
[4, 5, 'point {stroke-width: 2; stroke-color: red;}'],
]);
var options = {
legend: 'none'
};
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
chart.draw(data, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
I'm trying to use Miller Projection to convert coordinates to pixels.
My method looks like this:
function millerProjection(lat, lng) {
// Create sec() function //
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radian //
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
// var x = lng;
// var y = 1.25 * Math.log(Math.tan(Math.PI / 4 + 0.4 * (lat)));
// Mercator Projection
// var x = lng;
// var y = Math.log(Math.tan(lat) + sec(lat));
var mapSet = {
leftLong: toRadian(-180),
rightLong: toRadian(180),
topLat: toRadian(90),
bottomLat: toRadian(-90),
imageWidth: 2057,
imageHeight: 1512,
}
var x = (lng - mapSet.leftLong) * (mapSet.imageWidth / (mapSet.rightLong - mapSet.leftLong));
var y = (mapSet.topLat - lat) * (mapSet.imageHeight / (mapSet.topLat - mapSet.bottomLat));
console.log(`Miller Projection X: ${x} -- Y: ${y}`);
return { x: x, y: y };
}
I'm using this picture as a map:
https://upload.wikimedia.org/wikipedia/commons/5/5f/Miller_projection_SW.jpg
Apparently If I use 0, 0 coordinates it marks the correct location.
If I give it any other coordinates it's not working. Can the map be the problem or maybe there is an issue with the logic I use?
This:
var x = (lng - mapSet.leftLong) * (mapSet.imageWidth / (mapSet.rightLong - mapSet.leftLong));
var y = (mapSet.topLat - lat) * (mapSet.imageHeight / (mapSet.topLat - mapSet.bottomLat));
Assumes a linear x and y transform for both latitude and longitude, but this only occurs for longitude. You can see the different spacings between latitudes in the image you reference.
Let's go back to your commented out projection and use that as it is correct, but needs scaling and translation:
function millerProjection(lat, lng) {
// Create sec() function
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radians
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
var x = lng;
var y = 1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
return [x,y];
}
console.log(millerProjection(90,180));
console.log(millerProjection(-90,-180));
The output range for the x axis is -π to + π, and the y axis has a range about 0.733 times that.
Now we can scale and translate. I'll scale first and translate later, but vice versa is just as easy.
To scale, we need to know the width or height of the bounding box or output range. The aspect is fixed, so this is easiest if not specifying both, but rather determining one from the other. If we stretch the outputs unevenly, we no longer have a Miller.
Given a dimension for width we might scale with something like:
var scale = width / Math.PI / 2;
We want to see how many pixels are needed for each radian. Then we can multiple the projection output by the scale to get scaled values. Using the above, we can also validate our projection using a library like d3's geoprojection module:
function millerProjection(lat, lng) {
// Create sec() function
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radians
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
var x = lng;
var y = -1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
var width = 360;
var scale = width/Math.PI/2;
x *= scale;
y *= scale;
return [x,y];
}
////
// set up reference scale:
var width = 360;
var height = width / Math.PI / 2.303412543376391; // aspect ratio
// set d3 projection for reference:
var d3Miller = d3.geoMiller()
.fitSize([360,180*0.7331989845], {"type": "Polygon","coordinates": [[[180, 90], [-180, 90], [-90, -180], [180, 90]]] })
.translate([0,0]);
// compare the two:
console.log("90N,180W:")
console.log("Miller: ", ...millerProjection(90,-180));
console.log("d3Miller:", ...d3Miller([-180,90]));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-90,180));
console.log("d3Miller:", ...d3Miller([180,-90]));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-57,162));
console.log("d3Miller:", ...d3Miller([162,-57]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
I've taken the negative of the latitude (based on your example) because while projected geographic coordinates have y = 0 at the bottom - with y values increasing as one moves up. Conversely, things like (most?) images have y = 0 at the top - with y values increasing as one moves down. D3 anticipates the latter convention so I did not do it for the reference function
Looks good. Our data now has a range of -width/2 to width/2 on the x axis, and again on the y, about 0.733 times that. Let's translate the data so that it occupies the bounding box of with a bottom left coordinate of [0,0] and a top right coordinate of [width,width*0.733]. This is fairly easy, after we scale the data, we add width/2 to the x value, and width/2*0.733 (or slightly more precisely, width/2/*0.7331989845) to the y value:
function millerProjection(lat, lng) {
// Create sec() function
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radians
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
var x = lng;
var y = -1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
var width = 2057;
var scale = width/Math.PI/2;
x *= scale;
y *= scale;
x += width/2;
y += width/2*0.7331989845
return [x,y];
}
// compare the two:
console.log("90N,180W:")
console.log("Miller: ", ...millerProjection(90,-180));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-90,180));
console.log("45N,45E:")
console.log("Miller: ",...millerProjection(45,45));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-57,162));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
Of course, if you are doing image manipulation where the top should be y=0 and y values increase if you move down, flip the sign of the latitude before doing any manipulation.