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.
I want to then merge Layer X copy and Layer X.
Apparently there is a merge() function, but for that you have to crate a layerset. Not really a pro at javascript.
How to merge the two layers?
(function (){
var docRef = activeDocument
var newdLayer = docRef.activeLayer.duplicate();
newdLayer;
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var bounds = newdLayer.bounds;
var height = bounds[3].value - bounds[1].value;
var newSize = (100 / height) * 600;
newdLayer.resize(newSize, newSize, AnchorPosition.MIDDLECENTER);
app.preferences.rulerUnits = startRulerUnits;
var blur = docRef.activeLayer;
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var bounds = blur.bounds;
var width = bounds[2].value - bounds[0].value;
var newSize = (100 / width) * 900;
blur.resize(newSize, newSize, AnchorPosition.MIDDLECENTER);
app.preferences.rulerUnits = startRulerUnits;
blur.applyGaussianBlur(5)
blur;
})();
I modified your function by eliminating some redundant steps and added a snippet at the end that facilitates creating a new layerSet, moving the newdLayer and blur layers to the set, and merging that set:
(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;
})();
As a side note, a merge() method is available to artLayers as well, but depending on the number of layers your doc has and the order of the layers, it could require additional steps to merge the 2 layers with the artLayers method. This is because the artLayers merge() method simply merges whichever layer is active with the one below it. When layers are duplicated, I believe they are placed at index 0 in the artLayers collection, which would be the top most layer in the Layers palette. If your source layer wasn't already at index 0, then you'd need to sort layers prior to merge();
Related
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);
})
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 have a sidebar which shows the names of the markers in the current map view of a google map. The sidebar contents change as the map gets moved:
google.maps.event.addListener(map, 'bounds_changed', function() {
document.getElementById("list").innerHTML = "";
var mklen = mkrs.length,
a = 0,
bnds = map.getBounds();
for (a; a < mklen; a++) {
var themk = mkrs[a];
if (bnds.contains(themk.getPosition())) {
var myli = document.createElement("li");
myli.innerHTML = themk.title;
document.getElementById("list").appendChild(myli);
}
}
});
That's working OK, but the thing is that the bounds.contains() is very strict - if just the bottom tip of the marker is on the map (ie, you can't see 99% of it) it gets listed on the sidebar. What I'd like is to have just the markers that are completely shown pass that test.
There are a couple of approaches that I can think of and I can't believe that nobody else has come up against this problem, so I'm wondering if there is a preference out of the following:
take the bounds and recalculate them to be smaller than the actual bounds and use those new bounds for the bounds.contains() test
calculate where the edges of the marker icons are (I guess using fromDivPixelToLatLng) then check that both the ne AND sw corners are within the bounds and if so, list the item
Before you ask, I haven't tried either of those - I'm more looking for advice on which would be best or even possible, or if there is another way to do this. Here's a fiddle demonstrating the issue, in case it clarifies
In case anybody finds this later, I ended up recalculating the bounds - it seemed to be the approach that involved the least overhead. Here's the function:
function paddedBounds(npad, spad, epad, wpad) {
var SW = map.getBounds().getSouthWest();
var NE = map.getBounds().getNorthEast();
var topRight = map.getProjection().fromLatLngToPoint(NE);
var bottomLeft = map.getProjection().fromLatLngToPoint(SW);
var scale = Math.pow(2, map.getZoom());
var SWtopoint = map.getProjection().fromLatLngToPoint(SW);
var SWpoint = new google.maps.Point(((SWtopoint.x - bottomLeft.x) * scale) + wpad, ((SWtopoint.y - topRight.y) * scale) - spad);
var SWworld = new google.maps.Point(SWpoint.x / scale + bottomLeft.x, SWpoint.y / scale + topRight.y);
var pt1 = map.getProjection().fromPointToLatLng(SWworld);
var NEtopoint = map.getProjection().fromLatLngToPoint(NE);
var NEpoint = new google.maps.Point(((NEtopoint.x - bottomLeft.x) * scale) - epad, ((NEtopoint.y - topRight.y) * scale) + npad);
var NEworld = new google.maps.Point(NEpoint.x / scale + bottomLeft.x, NEpoint.y / scale + topRight.y);
var pt2 = map.getProjection().fromPointToLatLng(NEworld);
return new google.maps.LatLngBounds(pt1, pt2);
}
and you call it like this:
var padbnds = paddedBounds(50, 70, 100, 30);
specifying how much padding you want on the north, south, east and west edges of the map respectively