Leaflet bounds with padding - leaflet

I'm Trying to create caching mechanism instead of bringing the whole map.
right now i use:
getBounds() and receive all the points in the area the user is currently viewing.
What i would like to achieve is Padding for the Bounds.
for example:
map.getBounds(50)
should bring the bounds the user currently is viewing X2 the area.. or maybe one zoom level lower area.. but without interrupting the user.
currently fitBounds (JSFiddle Demo Here) does more or less that just resulting in changing the view of the user :( .. I Also tried getting the bounds from the function it self (fitBounds) but it outputs center and zoom not bounds (and i didnt succeed in resulting correct bounds from them)

Have you tried the pad method on the L.LatLngBounds you get from map.getBounds()? It is specifically made to increase the size of some bounds.
Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
For example if you want to increase the size of your bounds by 1 view port in each direction, you would do:
var newBounds = map.getBounds().pad(1)
Now if you want to double the area of the bounds (approximately, not taking projection into account), you would have to increase the total length in each axis by sqrt(2), hence increase the bounds in each direction by sqrt(2) / 2:
var doubleAreaBounds = map.getBounds().pad(Math.sqrt(2) / 2)

Related

How check if marker is within viewport in Leaflet, given center and zoom level?

Is it possible to check whether a point is within viewport using Leaflet?
I have center of viewport and zoom level. Is it possible to calculate boundaries having only those values?
I think you can use getBounds function of map and contains to check if marker is within viewport or not.
if(m.getBounds().contains(marker.getLatLng()))
//within viewport
I have center of viewport and zoom level. Is it possible to calculate boundaries having those values?
No.
One needs the size of the map (e.g. in pixels) in order to calculate the visible bounds of the map viewport.
Think about the edge cases: a map 0 pixels high and 0 pixels wide will never contain a marker within its bounds, whereas a map of infinite size on an infinitely large screen will always contain any marker.

How to calculate sizeDelta in RectTransform?

I write a custom content fitter that is required for my custom layout. So, I need to control RectTransform.sizeDelta property when anchors aren't same but I can't get that shows this value.
I don't need Unity3D API reference, I read it and got a nothing cuz it says only:
The size of this RectTransform relative to the distances between the
anchors. If the anchors are together, sizeDelta is the same as size.
If the anchors are in each of the four corners of the parent, the
sizeDelta is how much bigger or smaller the rectangle is compared to
its parent.
Can anyone explain in normal language what does it mean? And how can I calculate it manually when anchors aren't same?
The definition is somewhat confusing, indeed.
sizeDelta, basically, returns the difference between the actual rectangle of the UI element and the rectangle defined by the anchors.
For example, given a rectangle of 300x200:
Anchors in the same place as the corners of the rectangle: sizeDelta is (0,0)
Left or right anchors at half width of the rectangle: sizeDelta is (150,0)
All four anchors in a point: sizeDelta is (300,200) (i.e.: same size as the rectangle)
As you can see, it doesn't matter at all where the center of the rectangle defined by the anchors is, the only thing that matters is the difference between the width and height of the element rectangle and the anchors rectangle.
In pseudo-code, it's like this:
sizeDelta.x = UIElementRectangle.width - AnchorsRectangle.width;
sizeDelta.y = UIElementRectangle.height - AnchorsRectangle.height;
So, if the UI Rectangle has a dimension bigger than the anchors' one, sizeDelta is positive, if it's smaller, sizeDelta is negative.
sizeDelta: If you made a search, and end up here for an explanation of what sizeDelta means, like GetComponent().sizeDelta.y, then clear your mind.
Visualize a small PANEL, resting on top of a big CANVAS, it's Parent object.
In the PANEL's Rect Transform component, there are 2 rectangles defined:
(a) The rectangle defined by its Anchors. Those triangles. Normally related to the Parent Object location and dimensions, in this case the CANVAS.
(b) The rectangle defined by its own size, the PANEL's own dimension.
sizeDelta = (b) - (a)
That's it. Because normally an interactive component like a Button, smaller in size compared to the object where it rests, like a Panel, and because of that, normally sizeDelta is a negative value. Button size - Panel size = a negative value, normally.
You know the term Negative Space, used in general Design theory?
Think of it, as the space NOT used by a Button resting on a Panel.
Example:
How to find the height of a Panel, that is a Child of a Canvas that is a Camera overlay, thus screen sized. The Anchors of the Panel are related to the Canvas dimensions. Script is on the Panel object:
panelHeight = Screen.height + this.GetComponent().sizeDelta.y;
Remember, sizeDelta is normally negative so it reads more like this pseudo code:
panelHeight = Screen.height - this.sizeDelta.y
Hope this helps you, drove me crazy for a while. Cheers!
References:
https://www.youtube.com/watch?v=VhGxKDIKRvc
https://www.youtube.com/watch?v=FeheZqu85WI
public Vector2 ActualSize(RectTransform trans, Canvas can)
{
var v = new Vector3[4];
trans.GetWorldCorners(v);
//method one
//return new Vector2(v[3].x - v[0].x, v[1].y - v[0].y);
//method two
return RectTransformUtility.PixelAdjustRect(trans, canvas).size;
}
this function works in start

Get Latitude and Longitude of upper left corner of a Bing Map

I programatically create requests to dev.virtualearth.net (Bing static maps).
I know the following values:
Center Point (Latitude & Longitude)
Zoom Level
Map Size (X pixels, Y pixels)
After I recieved the map as a bitmap, how do I determine the Coordinates (Latitude and Longitude) of the upper left corner (basically the very first pixel) and the lower right corner (the very last pixel)?
I just need some suggestions or some pseudo code. Note, that while I know the Center Point, Zoom Level and Map Size, these aren't the same for every request.
Thank you.
You will need to do tile math: https://msdn.microsoft.com/en-us/library/bb259689.aspx
You will need to do the following:
Pass the center point into LatLongToPixelXY method to get the center global pixel value.
Knowing the pixel dimensions of the static image you created, subtract half the width from the x value of the center global pixel value. Do the same with the height and y.
This gives you a new pixel value, pass it into the PixelXYToLatLong to get the coordinate for the top left corner.
That's it :)
I have an old code sample that does this, but retrieves the static image using the old SOAP services rather than the REST services. You can find the blog post here: https://rbrundritt.wordpress.com/2008/10/25/ve-imagery-service-and-custom-icons/ See the LatLongToPixel function code that is half way down the post. That does the above three steps.

Multitouch swipe gesture

How can I simulate a two-finger left-to-right swipe within a UITextView in UIAutomation?
I've tried this:
var textView = target.frontMostApp().mainWindow().textViews()[0];
textView.dragInsideWithOptions({
touchCount:2,
startOffset:{x:0.2, y:5.80},
endOffset:{x:0.8, y:5.80},
duration:1.5
});
But apparently it is not having any effect. Sometimes it looks like it is doing only one finger gesture even I am passing 2 as the touch count.
One concern is: How UIAutomation knows where place the second touch? I am only specifying the start and end of one touch, not two. Is this correct? Maybe this is the origin of my problem?
Also, I have not very clear the coordinates system of objects. Unless I pass very small values for x, y I usually get warnings: out of bounds. Passing coordinates I would usually pass using Objective-C seem to be way large :(
A very late answer...
I met your problem today, in 2015, and I have the chance to have Apple doc for this function. Wasn't the case in 2012 perhaps...
Apple docs says :
You can use offsets to achieve finer precision in specifying the hitpoint within the rect for the specified element. The offset comprises a pair of x and y values, each ranging from 0.0 to 1.0. These values represent, respectively, relative horizontal and vertical positions within the rect, with {x:0.0, y:0.0} as the top left and {x:1.0, y:1.0} as the bottom right. Thus, {x:0.3, y:0.6} specifies a position just below and to the left of center, and {x:1.0, y:0.5} specifies a position centered vertically at the far right.
This example performs a slow drag within the target element from left edge to right edge, just below the top:
target.dragInsideWithOptions({startOffset:{x:0.0, y:0.1}, endOffset:{x:1.0, y:0.1}, duration:1.5});
So x and y values are relative within the rect you choose.
In your example :
startOffset:{x:0.2, y:5.80}
You begin the swipe from left in the textview at 20% of its length, and y is 5.80 times beyond the border...
It would better work with values as :
startOffset:{x:0.0, y:0.1}, endOffset:{x:0.5, y:0.1}
For my part, I made the two-finger left-to-right swipe work with this code :
target.frontMostApp().mainWindow().dragInsideWithOptions({touchCount:2, startOffset:{x:0.0, y:0.1}, endOffset:{x:0.5, y:0.1}, duration:1});

MKMapView setRegion "snaps" to predefined zoom levels?

Can anyone confirm that setRegion "snaps" to predefined zoom levels and whether or not this behavior is as designed (although undocumented) or a known bug? Specifically, it appears that setRegion snaps to the same zoom levels that correspond to the zoom levels used when the user double-taps the map.
I'm trying to restore a previously saved region but this behavior makes it impossible if the saved region was set via a pinch zoom and not a double-tap zoom.
A big clue to me that things are broken on the mapkit side is what occurs if I call regionThatFits on the map's current region. It should return the same region (since it obviously fits the map's frame) but it returns the region that corresponds to the next higher predefined zoom level instead.
setVisibleMapRect behaves similarly.
Any further insight or information would be appreciated.
I found these related posts but neither included a solution or definitive confirmation that this is in fact a mapkit bug:
MKMapView setRegion: odd behavior?
MKMapView show incorrectly saved region
EDIT:
Here is an example that demonstrates the problem. All values are valid for my map view's aspect ratio:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = 47.700200f;
initialRegion.center.longitude = -122.367109f;
initialRegion.span.latitudeDelta = 0.065189f;
initialRegion.span.longitudeDelta = 0.067318f;
[map setRegion:initialRegion animated:NO];
NSLog(#"DEBUG initialRegion: %f %f %f %f", initialRegion.center.latitude, initialRegion.center.longitude, initialRegion.span.latitudeDelta, initialRegion.span.longitudeDelta);
NSLog(#"DEBUG map.region: %f %f %f %f", map.region.center.latitude, map.region.center.longitude, map.region.span.latitudeDelta, map.region.span.longitudeDelta);
OUTPUT:
DEBUG initialRegion: 47.700199 -122.367111 0.065189 0.067318
DEBUG map.region: 47.700289 -122.367096 0.106287 0.109863
Note the discrepancy in the latitude/longitude delta values. The map's values are almost double what I requested. The larger values correspond to one of the zoom levels used when the user double-taps the map.
Yes, it snaps to discrete levels. I've done quite a bit of experimentation, and it seems to like multiples of 2.68220906e-6 degrees of longitude per pixel.
So if your map fills the whole width of the screen, the first level spans .0008583 degrees, then the next level up you can get is twice that, .001717, and then the next one is twice that, .003433, and so on. I'm not sure why they chose to normalize by longitude, it means that fixes zoom levels vary depending on what part of the world you are looking at.
I've also spent a lot of time trying to understand the significance of that number .68220906e-6 degrees. It comes out to about 30cm at the equator, which kind of makes sense since the high resolution photos used by Google Maps have a 30cm resolution, but I would have expected them to use latitude instead of longitude to establish the zoom levels. That way, at maximum zoom, you always the native resolution of the satellite images, but who knows, they probably have some smart-people reason for making it work like that.
In my application I need to display a certain range of latitude. I'm gonna work on some code to try to zoom the map as close as possible to that. If anyone is interested, contact me.
I found a solution.
If the received snapped zoom level, is, lets say a factor of 1.2 bigger than the desired one:
use this algorithm to correct:
Asumption: you want to set the map view to exactly show "longitudinalMeters" from left to right
1) Calculate the correction scale:
Calculate the relation between longitudinal span you received, to that one you have got.
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, 0, longitudinalMeters);
MKCoordinateRegion regionFits = [mapView regionThatFits: region];
double correctionFactor = regionFits.span.longitudeDelta / region.span.longitudeDelta;
2) Create the transformation and apply it to the map
CGAffineTransform mapTransform = CGAffineTransformMakeScale(correctionScale, correctionScale);
CGAffineTransform pinTransform = CGAffineTransformInvert(mapTransform);
[mapView setTransform:mapTransform];
3) Apply the inverse transformation to the Map pins, to keep them at original size
[mapView setTransform:mapTransform];
for (id<MKAnnotation> annotation in self.mapView.annotations)
{
[[self.mapView viewForAnnotation:annotation] setTransform:pinTransform];
}
The weird behavior seems to be due to the fact that while one requests a particular region or view size, the actual API call to google is invoked with a center point and a zoom level. E.G.:
map.setCenter(new google.maps.LatLng(234.3453, 454.2345), 42);
Now it would be possible for Apple to request the appropriate zoom level and then adjust the sizing of the view to accommodate the actual region request, but it seems they fail to do so. I am drawing bus routes on a map, and one of my routes barely triggers a larger zoom level and thus scales too small (under-zooms) and looks ugly and smashed.
#pseudopeach, Please update me on the progress of your attempts to work around this issue. If one could detect the boundaries of a zoom level, the region request could then be deliberately underscaled to avoid the under-zoom. Since you are onto this I would be interested in seeing your code before I have to make an attempt at it myself.
There is an interesting category that the author of the blog Backspace Prolog has written to enable the direct manipulation of the Google Maps API by emulating their setCenter(centerPoint,ZoomLevel) call signature. You can find it here. I haven't spent the time yet, but the math can probably be reverse engineered to yield a means of calculating the zoom level for a given Region or MapRect. Depending on how far it is within the zoom level's range - i.e. how far it is over the threshold that triggers the lower zoom level - it could decide whether to go to the lower level or keep to higher one by under-requesting.
This is clearly a behavioral bug that needs to be fixed so that MKMapView can be used in a more refined manner.
This is an old question, but I recently investigated Google maps in detail, and can share some insight. I don't know whether this is also valid for the current Apple maps.
The reason that the resolution snaps to predefined zoomlevels is because the original maps fetched from Google's servers are drawn with those zoomlevels. The size of the features on those maps are drawn with a certain resolution in mind. For example, the width (in pixels) of a road on those maps is always the same. On higher resolution maps, more secundary roads are drawn, but their width is always the same. The resolution snaps to predefined levels to make sure those features are always depicted with the same size. That is, it is not a bug but a feature.
Those predefined resolutions vary with latitude because of the Mercator projection of the maps. Mercator projection is easy to work with because latitude lines are depicted straight and horizontal and longitude lines are straight and vertical. But with Mercator projection the top of the map has a slightly higher resolution than the bottom (on the Northern hemisphere). That has consequences for fitting maps together at the northern and sourthern edges.
That is, when you start on the equator and drive north, then the resolution of the Mercator maps you drive over will gradually increase. The longitude lines remain vertical, and therefore the longitude spans remains the same. But the resolution increases, and therefore the latitude span decreases. Still, on all those maps the roads have the same width in pixels, and texts are depicted in the same font size, etc.
Google uses a Mercator projection where the equator circumference is 256 pixels at zoomlevel 0. Each next zoomlevel doubles that amount. That is, at zoomlevel 1, the equator is 512 pixels long, at zoomlevel 2, the equator is 1024 pixels long, etc. The model for the earth they use is a FAI globe with a radius of exactly 6371 km, or circumference of 40030 km.
Therefore, resolution for zoomLevel 0 at the equator is 156.37 km/pixel, at zoomlevel 1 it is 78.19 km/pixel, etc. Those resolutions then vary with the cosinus of the latitude anywhere else on the earth.
MKCoordinateRegion region;
region.center.latitude = latitude;
region.center.longitude = longitude;
region.span.latitudeDelta = 5.0;
region.span.longitudeDelta = 5.0;
[mapView setRegion:region animated:YES];
I restore the region with no problem and with no variance as you describe. It is really impossible to tell what is specifically wrong in your case without some code to look at but here's what works for me:
Save both the center and span values somewhere. When you are restoring them specifically set both the center and span.
Restoring should look like this:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = Value you've stored
initialRegion.center.longitude = Value you've stored
initialRegion.span.latitudeDelta = Value you've stored
initialRegion.span.longitudeDelta = Value you've stored
[self.mapView setRegion:initialRegion animated:NO];
Also remember that this method is available in 4.0: `mapRectThatFits:edgePadding: MapRectThatFits helpfully adds a reasonable border to ensure that say a map annotation on the edge is not obscured and the the rect that you're attempting to display is fully visible. If you want to control the border use the call that gives you access to set edgePadding as well.
If you set up the MapView in InterfaceBuilder, make sure you don't do this:
_mapView = [[MKMapView alloc] init];
As soon as I removed this init line, my map view suddenly began responding properly to all the updates I sent it. I suspect that what happens is that if you do the alloc init, it's actually creating another view that's not being shown anywhere. The one you see on the screen is the one initialized by your nib. But if you alloc init a new one, then that's something somewhere else and it's not going to do anything.