Open timblack1 opened 8 years ago
Doug, I'm going to leave my notes here for you so you can work on the algebraic refactoring when you have time.
We start with this:
ratio_milesperzoomlevel = dis/zoom
Our goal: get new zoom level
So it seems this would be the right formula:
zoom * ratio_milesperzoomlevel = zoom * dis/zoom zoom = dis/ratio_milesperzoomlevel
But that doesn't work, because according to docs describing Google Maps, when the zoom goes up linearly, miles go down quadratically, as is represented in the following table.
zoom | miles | divide by |
---|---|---|
0 | 24000 | 1 |
1 | 12000 | 2 |
2 | 6000 | 4 |
3 | 3000 | 8 |
4 | 1500 | 16 |
5 | 750 | |
6 | 375 | |
7 | 165 |
So the above table can be represented by the following formula. How can we factor out z to be the only term on the left side of the equation?
new_radius_miles = original_radius_miles/(2^z)
(2^z)*new_radius_miles = original_radius_miles
(2^z) = original_radius_miles/new_radius_miles
TODO: Start here. How can we factor out z?
z = ?
The following might work:
(2^new_zoom) = original_radius_miles/new_radius_miles
We can factor out new_zoom using the following formula: For a = b^c, c = log b of a, or c = Math.log(a) / Math.log(b)
var new_zoom = Math.log(dis/Number(this.search_radius)) / Math.LN2;
Try:
(2^z) = original_radius_miles/new_radius_miles
// For a = b^c, c = log b of a, or c = Math.log(a) / Math.log(b)
z = Math.log10(original_radius_miles/new_radius_miles) / Math.log10(2)
This is the order in which the user's interaction works. First, the interface has a zoom level. Then the user selects a new search radius (in miles), which moves the user's selection up or down the rows of the table below. We need to calculate the new zoom level which results.
miles | zoom | divide original miles by (shows quadratic progression) |
---|---|---|
24000 | 0 | 1 |
12000 | 1 | 2 |
6000 | 2 | 4 |
3000 | 3 | 8 |
1500 | 4 | 16 |
750 | 5 | 32 |
375 | 6 | 64 |
187.5 | 7 | 128 |
When miles go down by 1/2, zoom goes up by 1. When miles go up by 2x, zoom goes down by 1. We need to translate this into a function which takes in a new number of miles selected by the user, and outputs a new zoom level.
newMiles = oldMiles/(2^(newZoom - oldZoom)) (2^(newZoom - oldZoom)) * newMiles = oldMiles 2^(newZoom - oldZoom) = oldMiles/newMiles 2^(newZoom - oldZoom) = oldMiles/newMiles log_2(oldMiles/newMiles) = newZoom - oldZoom log_2(oldMiles/newMiles) + oldZoom = newZoom
Check out the Intro to Logarithms at https://www.khanacademy.org/math/algebra-home/alg-exp-and-log.
We've implemented this partially, and paused development until we hear back from https://github.com/GoogleWebComponents/google-map/issues/339 as to whether we can use non-integer zoom values. For now we just round search radii to the nearest integer zoom level, which is not as fine a resolution as we would like. We did this so we could merge the feature branch and not let it get far behind the develop
branch. We hope to come back to this later.
Problem: Our current algorithm for calculating the zoom level seems to be correct, so long as the map's pixel dimensions remain the same. If the user resizes the window, making the map change pixel dimensions, then the calculations are not accurate anymore.
This raises the question, is the map's initial zoom level correctly coordinated with the initial search radius entered in the search radius input? If so, how can we be sure it will be correct on different screen sizes? If not, then the algorithm may be incorrect.
Here's a recommendation for how to get the right algorithm:
If the viewing area is defined in Lat/Lng format, The distortion happens when it is populated with the appropriate Mercator projections. Instead, the correct viewing box must be defined in Mercator or Project Mercator.
@DouglasHuston, is the point of your last comment on here (at https://github.com/timblack1/rcl/issues/154#issuecomment-280792480) that we need to use the Mercator projection?
If we do use the Mercator projection, we must define the viewing box correctly, and not just use the Lat/Lng format. This does not mean we are restricted to only using the Mercator projection.
Plan A
Maaayyybe an easy solution would be to:
And you're done!
Note: One motivation for this method is that it appears Google's Maps API's zoom levels only exist in discrete integers, and can't actually be set in finer (fractional or decimal) increments.
Note: This implies when the user selects a search radius, we need to hide all the congregations which don't fit in that radius, and more importantly, when the user pans or zooms the map, we need to SHOW all the new congregations which fit within the map's new bounds, and stop using the circle as the bounds within which to display congregations. In other words, the map needs two modes here:
1) search radius/circle mode, and 2) panning-zooming/bounding box mode.
Plan B
Or, if we want to be all complicated like we used to think we had to, we could try any of the following:
It looks like the zoom levels don't correspond directly to miles. See http://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to, http://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds for information on how to translate from miles to zoom levels.
Some example algorithms:
http://jeffjason.com/2011/12/google-maps-radius-to-zoom/ http://stackoverflow.com/questions/5162950/google-maps-api-v3-set-zoom-level-to-show-a-given-radius
Note: Google Maps uses the "Google Web Mercator" projection and coordinate system. Its official EPSG identifier is EPSG:3857. It uses spherical rather than the standard Mercator ellipsoid formulae. According to https://en.wikipedia.org/wiki/Web_Mercator#Properties, "While the Web Mercator's formulas are for the spherical form of the Mercator, geographical coordinates are required to be in the WGS 84 ellipsoidal datum." Formulae are available at https://en.wikipedia.org/wiki/Web_Mercator#Formulas. (Question: Do x & y values there refer to the bounding box's height & width in pixels?) Note there are a couple more formulae in a JavaScript-like language at the end of this article: https://alastaira.wordpress.com/2011/01/23/the-google-maps-bing-maps-spherical-mercator-projection/.
See general Google Maps coordinate overview at https://developers.google.com/maps/documentation/javascript/maptypes?csw=1#MapCoordinates.
This might work:
When a user changes the search radius (in miles/km):
computeDistanceBetween()
to do the same.Here is a Great Circle Distance formula: http://stackoverflow.com/questions/3525670/radius-of-viewable-region-in-google-maps-v3.