I use the polymaps library to display a custom tiled map. Whenever the user clicks on it, I need to know the coordinates of that point. Not the lat/lon values that map.mouse(e) gives, but pixel coordinates.
var po = org.polymaps;
var div = document.getElementById("map");
var map = po.map()
.container(div.appendChild(po.svg("svg")))
.zoomRange([0,8])
.zoom(1)
.add(po.image()
.url(getTile));
map.add(po.interact())
.add(po.hash());
$(div).mousemove(function(e) {
???
})
Does this library provide a function to do this?
To obtain the mouse position on the original image from which the tilemap was created, use the following code:
$(div).mousemove(function(e) {
// Get column, row and zoom level of mouse position
var crz = map.locationCoordinate(map.pointLocation(map.mouse(e)));
// Normalize the column and row values to the 0..1 range
var zoomMultiplier = Math.pow(2, crz.zoom-1);
var x01 = crz.column/zoomMultiplier;
var y01 = crz.row/zoomMultiplier;
// Multiply with the original image width and height
var originalWidth = 32768;
var originalHeight = 32768;
var mx = x01*originalWidth;
var my = y01*originalHeight;
// Now we have the mouse coordinates on the original image!
console.log(mx, my);
})
Related
My Photoshop Canvas is 900X600.
The function below takes Layer X and makes Layer X copy.
It takes Layer X copy and while maintaining the ratio adjusts the height to 600px. var newdLayer
It takes Layer X and while maintaining the ratio adjusts the width to 900px and applies the Gaussian Blur. var blur.
It then merges Layer X and Layer X copy.
The problem is that if Layer X is not centered in the beginning of the script then it malfunctions.
How to add center layer to Layer X in the beginning of the script?
(function (){
var docRef = activeDocument
var blur = docRef.activeLayer;
var newdLayer = blur.duplicate();
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// since we resize based on the initial size of the source layer,
// we don't need to get the bounds twice
var bounds = blur.bounds;
var height = bounds[3].value - bounds[1].value;
var width = bounds[2].value - bounds[0].value;
// declare 2 different vars for your sizes (there are better ways to do this, but
// since you say you aren't a JavaScript pro, I figured I'd keep it simple)
var newSize600 = (100 / height) * 600;
var newSize900 = (100 / width) * 900;
// resize your layers
newdLayer.resize(newSize600, newSize600, AnchorPosition.MIDDLECENTER);
blur.resize(newSize900, newSize900, AnchorPosition.MIDDLECENTER);
// apply blur
blur.applyGaussianBlur(5);
// below creates the group, moves the layers to it and merges them. Feel free to just include this part
// at the end of your function if you don't want to use the modified code above.
// create a new layer set
var groupOne = docRef.layerSets.add();
// move the blur layer inside the layer set and name the layer for posterity
blur.move(groupOne, ElementPlacement.INSIDE);
blur.name = "blur";
// move the newdLayer inside and rename
newdLayer.move(groupOne, ElementPlacement.INSIDE);
newdLayer.name = "newdLayer";
// merge the layer set and name the new layer
var mergedGroup = groupOne.merge();
mergedGroup.name = "newdLayer + blur";
app.preferences.rulerUnits = startRulerUnits;
})();
What you need to do is to calculate difference between layer center and document center and then translate the blur layer by that difference before making a copy.
To calculate the layer center you take bound[0].value and bound[1].value (distances from the left top corner of the document to the top left corner of the layer) and add half of the width and half of the height. And then to calculate the deltas you subtract layer center coordinates from document center coordinate.
Here's the code:
(function()
{
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var docRef = activeDocument;
var blur = docRef.activeLayer;
// since we resize based on the initial size of the source layer,
// we don't need to get the bounds twice
var bounds = blur.bounds;
var height = bounds[3].value - bounds[1].value;
var width = bounds[2].value - bounds[0].value;
/////////////////////////////////////////////////////////////////////////////////////
// Centering the layer
// Getting center coordinates of the document
var docCenterW = docRef.width.as("px") / 2;
var docCenterH = docRef.height.as("px") / 2;
// getting values to translate the layer.
var deltaX = Math.round(docCenterW - (bounds[0].value + width / 2));
var deltaY = Math.round(docCenterH - (bounds[1].value + height / 2));
blur.translate(deltaX, deltaY);
/////////////////////////////////////////////////////////////////////////////////////
var newdLayer = blur.duplicate();
// declare 2 different vars for your sizes (there are better ways to do this, but
// since you say you aren't a JavaScript pro, I figured I'd keep it simple)
var newSize600 = (100 / height) * 600;
var newSize900 = (100 / width) * 900;
// resize your layers
newdLayer.resize(newSize600, newSize600, AnchorPosition.MIDDLECENTER);
blur.resize(newSize900, newSize900, AnchorPosition.MIDDLECENTER);
// apply blur
blur.applyGaussianBlur(5);
// below creates the group, moves the layers to it and merges them. Feel free to just include this part
// at the end of your function if you don't want to use the modified code above.
// create a new layer set
var groupOne = docRef.layerSets.add();
// move the blur layer inside the layer set and name the layer for posterity
blur.move(groupOne, ElementPlacement.INSIDE);
blur.name = "blur";
// move the newdLayer inside and rename
newdLayer.move(groupOne, ElementPlacement.INSIDE);
newdLayer.name = "newdLayer";
// merge the layer set and name the new layer
var mergedGroup = groupOne.merge();
mergedGroup.name = "newdLayer + blur";
app.preferences.rulerUnits = startRulerUnits;
})();
I've created following map using MapKit.js, with hundred of custom annotations, clustering (yellow dots) and callout popup on annotation click.
What I want to do, when clicking on the popup link, is simply to zoom in one step and center view on the clicked annotation (in a responsive context).
In Google Maps, that I'm used to, you simply position map by it's center and zoom level.
In MapKit.js, you use a center/region combo, and honestly I can't understand how this works.
Official doc is unclear to me, and I wasn't able to find really enlightling ressource.
If someone could explain to me how we are supposed to manage zoom level using center / region combo, it would be really appreciated.
Thanks :-)
[EDIT]
This center/region thing still doesn't make sense to me, so I've decided to override MapKit.js with a zoom feature.
Thanks to this post, I've manage to implement the zoom calculation, which seems to be ok.
I need now to implement the set zoom action.
No success yet, this math things are so far now ^^
Any help is highly welcomed :-)
Function:
function MapKitJsZoom(map) {
var LN2 = 0.6931471805599453; // ???
var WH = 256; // World Height
var WW = 256; // World Width
var MAX = 21; // Max zoom level
// GET CURRENT ZOOM.
var latToRad = function (lat) {
var sin = Math.sin(lat * Math.PI / 180);
var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
};
var zoom = function (mapPx, worldPx, fraction) {
return (Math.log(mapPx / worldPx / fraction) / LN2);
};
this.get = function () {
var bounds = map.region.toBoundingRegion();
var latFraction = (latToRad(bounds.northLatitude) - latToRad(bounds.southLatitude)) / Math.PI;
var latZoom = zoom(map.element.clientHeight, WH, latFraction);
var lngDiff = bounds.eastLongitude - bounds.westLongitude;
var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
var lngZoom = zoom(map.element.clientWidth, WW, lngFraction);
return Math.round(Math.min(latZoom, lngZoom, MAX));
};
// SET CURRENT ZOOM
this.set = function (zoom) {
// TODO
// I need to calculate latitude and longitude deltas
// that correspond to required zoom based on viewport size
// (map.element.clientWidth and map.element.clientHeight)
map.region.span = new mapkit.CoordinateSpan(latitudeDelta, longitudeDelta);
};
}
Usage:
var map = new mapkit.Map("map");
map.zoom = new MapKitJsZoom(map);
map.addEventListener('region-change-end', function () {
console.log(map.zoom.get());
});
There are two methods of accomplishing this:
1) set center then change zoom level
var newCenter = new mapkit.Coordinate(37.792446, -122.399360);
map._impl.zoomLevel--;
map.setCenterAnimated(newCenter, true);
2) set region using center and span (delta in degress)
var newCenter = new mapkit.Coordinate(37.792446, -122.399360);
var span = new mapkit.CoordinateSpan(.01);
var region = new mapkit.CoordinateRegion(newCenter, span);
map.setRegionAnimated(region)
I am building a map in d3 and basing it off of this codepen by Andy Barefoot: https://codepen.io/nb123456/pen/zLdqvM?editors=0010. I want to modify the initiateZoom() function so that if I set the lat/lon coordinates for a box surrounding say Ohio, the map will initialize its panning to be over Ohio.
function initiateZoom() {
minZoom = Math.max($("#map-holder").width() / w, $("#map-holder").height() / h);
maxZoom = 20 * minZoom;
zoom
.scaleExtent([minZoom, maxZoom])
.translateExtent([[0, 0], [w, h]])
;
midX = ($("#map-holder").width() - minZoom * w) / 2;
midY = ($("#map-holder").height() - minZoom * h) / 2;//These are the original values
var swlat = 32;
var swlon = -82;
var nelat = 42;
var nelon = -72;
var projectCoordinates = projection([(swlat+nelat)/2, (swlon+nelon)/2]);
/*This did not work
var midX = minZoom*(w-(swlat+nelat)/2) - ($("#map-holder").width()-(swlat+nelat)/2);
var midY = minZoom*(h-(swlon+nelon)/2) - ($("#map-holder").height()-(swlon+nelon)/2);*/
/*Neither did this
var midX = minZoom*(w-projectCoordinates[0])-($("#map-holder").width()-projectCoordinates[0]);
var midY = minZoom*(h-projectCoordinates[1])-($("#map-holder").height()-projectCoordinates[1]);*/
svg.call(zoom.transform, d3.zoomIdentity.translate(midX, midY).scale(minZoom));
}
The idea behind the original approach was to:
1: Get the current pixel display of the map
2: Get the new pixel distance from the map corner to the map point after zoom has been applied
3: The pixel distance of the center of the container to the top of the container
4: subtract the values from 2 and 3
The original post was trying to translate the map so that it would initialize the zoom and pan over the center of the map. I tried to modify this approach first by directly substituting the lat/lon values into the above equations. I also tried first transforming the lat/lon values using the projection and then substituting those values in, with little success. What do I need to do in order to get my desired result?
Setting a translateExtent could be a bad idea because it depends on the zoom scale.
The following replacement works.
function initiateZoom() {
// Define a "minzoom" whereby the "Countries" is as small possible without leaving white space at top/bottom or sides
minZoom = Math.max($("#map-holder").width() / w, $("#map-holder").height() / h);
// set max zoom to a suitable factor of this value
maxZoom = 20 * minZoom;
// set extent of zoom to chosen values
// set translate extent so that panning can't cause map to move out of viewport
zoom
.scaleExtent([minZoom, maxZoom])
.translateExtent([[0, 0], [w, h]])
;
var swlat = 32;
var swlon = -82;
var nelat = 42;
var nelon = -72;
var nwXY = projection([swlon, nelat]);
var seXY = projection([nelon, swlat]);
var zoomScale = Math.min($("#map-holder").width()/(seXY[0]-nwXY[0]), $("#map-holder").height()/(seXY[1]-nwXY[1]))
var projectCoordinates = projection([(swlon+nelon)/2, (swlat+nelat)/2]);
svg.call(zoom.transform, d3.zoomIdentity.translate($("#map-holder").width()*0.5-zoomScale*projectCoordinates[0], $("#map-holder").height()*0.5-zoomScale*projectCoordinates[1]).scale(zoomScale));
}
I am trying to determine a way to calculate the number of meters represented by 1 pixel at a given zoom level and geo centerpoint in Leaflet. Could anyone direct me to the math involved or if there is a way to do this out of the box in leaflet? I am not finding much out there.
You can use the containerPointToLatLng conversion function of L.Map to get the latLngcoordinates for a given pixelcoordinate. If you take one of the first pixel, and one of the next, you can use the distanceTo utility method of L.LatLng to calculate the distance in meters between them. See the following code (assuming map is an instance of L.Map):
var centerLatLng = map.getCenter(); // get map center
var pointC = map.latLngToContainerPoint(centerLatLng); // convert to containerpoint (pixels)
var pointX = [pointC.x + 1, pointC.y]; // add one pixel to x
var pointY = [pointC.x, pointC.y + 1]; // add one pixel to y
// convert containerpoints to latlng's
var latLngC = map.containerPointToLatLng(pointC);
var latLngX = map.containerPointToLatLng(pointX);
var latLngY = map.containerPointToLatLng(pointY);
var distanceX = latLngC.distanceTo(latLngX); // calculate distance between c and x (latitude)
var distanceY = latLngC.distanceTo(latLngY); // calculate distance between c and y (longitude)
That should work, thanks to Jarek PiĆ³rkowski for pointing my mistake before the edit.
You can use this to work out the metres per pixel:
metresPerPixel = 40075016.686 * Math.abs(Math.cos(map.getCenter().lat * Math.PI/180)) / Math.pow(2, map.getZoom()+8);
Take a look at openstreetmap.org page on zoom levels. It gives this formula for calculating the meters per pixel:
The distance represented by one pixel (S) is given by
S=C*cos(y)/2^(z+8) where...
C is the (equatorial) circumference of the Earth
z is the zoom level
y is the latitude of where you're interested in the scale.
Correct me if I am wrong, IMHO, the number of meters per pixel = map height in meters / map height in pixels
function metresPerPixel() {
const southEastPoint = map.getBounds().getSouthEast();
const northEastPoint = map.getBounds().getNorthEast();
const mapHeightInMetres = southEastPoint.distanceTo(northEastPoint);
const mapHeightInPixels = map.getSize().y;
return mapHeightInMetres / mapHeightInPixels;
}
I'm developing a hex-grid strategy game in HTML5, and since it becomes complicated to add more buttons to UI, I'm rewriting my code using KineticJS to draw the canvas.
In my game, there is a minimap, and a rectangle in it showing the position of player's camera. When the player clicks on the minimap, the center of his camera will be set to the position he clicked. My original code does not use any external library, and it looks like this:
this.on('click', function(event){
var canvas = document.getElementById('gameCanvas');
var x = event.pageX - canvas.offsetLeft;
var y = event.pageY - canvas.offsetTop;
camera.setPos( // calculate new position based on x and y....);
}
})
So basically what I want is the POSITION OF CLICK EVENT RELATIVE TO THE CANVAS. Since now KineticJS takes control of the canvas, and it does not let me set canvas id, I can't use getElementById to choose my canvas anymore. Is there another way to do it?
There might be some way I did not figure out that can set the canvas id, but I'm expecting a more elegent solution, idealy through KineticJS API.
Thanks.
To get the mouse position on the stage (canvas), you can use:
var mouseXY = stage.getPointerPosition();
var canvasX = mouseXY.x;
var canvasX = mouseXY.y;
You can get the mouse position inside a shape click the same way:
myShape.on('click', function() {
var mouseXY = stage.getPointerPosition();
var canvasX = mouseXY.x;
var canvasY = mouseXY.y;
});
Good question, and I think what you are looking for is getAbsolutePosition().
The following is useful things to know from my experience.
getPosition()
node x/y position relative to parent
if your shape is in layer, x/y position relative to layer
if your shape is in group, x/y position relative to group
getParent()
To know what parent node is
getX()
x position relative to parent
getY()
y position relative to parent
getAbsolutePosition()
absolute position relative to the top left corner of the stage container div
Few more useful things,
getScale()
if your stage is zoomed in or out, better to know this.
Everything is here in document, http://kineticjs.com/docs/symbols/Kinetic.Node.php
This is an old question, but as I can see, the best answer does not take into account the rotation.
So here it is my solution based on Kinetic v5.1.0. who take into account all transformation.
var myStage = new Kinetic.Stage();
var myShape = new Kinetic.Shape(); // rect / circle or whatever
myShape.on('click', function(){
var mousePos = myStage.getPointerPosition();
var p = { x: mousePos.x, y: mousePos.y }; // p is a clone of mousePos
var r = myShape.getAbsoluteTransform().copy().invert().point(mousePos);
// r is local coordinate inside the shape
// mousePos is global coordinates
})
I found out the best way to do it is...
var bgxy = bg.getAbsolutePosition();
var x = event.pageX - stage.getContainer().offsetLeft - bgxy.x;
var y = event.pageY - stage.getContainer().offsetTop - bgxy.y;
You should use event.layerX and event.layerY :
this.on('click', function(event){
var canvas = document.getElementById('gameCanvas');
var x = event.layerX;
var y = event.layerY;
camera.setPos( // calculate new position based on x and y....);
}
})