From 7a9a8d3fb7f3d1bf4409bddd326f2cc0545857cc Mon Sep 17 00:00:00 2001 From: Lukyanov Fedor Date: Wed, 30 Nov 2016 22:13:19 +0200 Subject: [PATCH] Add ne and sw to bounds object (#287) * Add ne and sw to bounds object * Add ability to use ne and sw in fitBounds * Return all corners from fitBounds * Remove exports object in favour export --- README.md | 21 +++- src/google_map.js | 16 +++ src/utils/geo.js | 6 +- src/utils/utils.js | 246 ++++++++++++++++++++++++++------------------- 4 files changed, 178 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index a0d810f..ac80a50 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ UMD AMD and other build are available under dist folder after `npm install` ## What's it Look Like? -In the simple case you just need to add `lat` `lng` props to any child of `GoogleMap` component. +In the simple case you just need to add `lat` `lng` props to any child of `GoogleMap` component. [simple example in action](http://istarkov.github.io/google-map-react/map/simple) ```javascript @@ -318,6 +318,19 @@ const bounds = { } }; +// Or + +const bounds = { + ne: { + lat: 50.01038826014866, + lng: -118.6525866875 + }, + sw: { + lat: 32.698335045970396, + lng: -92.0217273125 + } +}; + const size = { width: 640, // Map width in pixels height: 380, // Map height in pixels @@ -430,10 +443,10 @@ function createMapOptions() { --- -(*Really big thanks to [April Arcus](https://github.com/AprilArcus) for documentation fixes*) -(*thank you [Dan Abramov](http://gaearon.github.io/react-dnd/) for titles structure*) +(*Really big thanks to [April Arcus](https://github.com/AprilArcus) for documentation fixes*) +(*thank you [Dan Abramov](http://gaearon.github.io/react-dnd/) for titles structure*) (*great thanks to [Vladimir Akimov](https://github.com/b2whats) he knows why*) ## License -MIT (http://www.opensource.org/licenses/mit-license.php) +MIT (http://www.opensource.org/licenses/mit-license.php) diff --git a/src/google_map.js b/src/google_map.js index 5ec3db6..cf52152 100644 --- a/src/google_map.js +++ b/src/google_map.js @@ -899,6 +899,14 @@ export default class GoogleMap extends Component { lat: bounds[2], lng: bounds[3], }, + sw: { + lat: bounds[4], + lng: bounds[5], + }, + ne: { + lat: bounds[6], + lng: bounds[7], + }, }, marginBounds: { nw: { @@ -909,6 +917,14 @@ export default class GoogleMap extends Component { lat: marginBounds[2], lng: marginBounds[3], }, + sw: { + lat: marginBounds[4], + lng: marginBounds[5], + }, + ne: { + lat: marginBounds[6], + lng: marginBounds[7], + }, }, size: this.geoService_.hasSize() diff --git a/src/utils/geo.js b/src/utils/geo.js index a0c77b3..2519a7a 100644 --- a/src/utils/geo.js +++ b/src/utils/geo.js @@ -97,8 +97,10 @@ export default class Geo { }); let res = [ - topLeftCorner.lat, topLeftCorner.lng, - bottomRightCorner.lat, bottomRightCorner.lng, + topLeftCorner.lat, topLeftCorner.lng, // NW + bottomRightCorner.lat, bottomRightCorner.lng, // SE + bottomRightCorner.lat, topLeftCorner.lng, // SW + topLeftCorner.lat, bottomRightCorner.lng, // NE ]; if (roundFactor) { diff --git a/src/utils/utils.js b/src/utils/utils.js index c808971..9209054 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -63,111 +63,147 @@ function meters2WorldSize(meters, { lat, lng }) { return { w, h }; } -const exports = { - fitBounds({ nw, se }, { width, height }) { - const EPS = 0.000000001; - const nwWorld = latLng2World(nw); - const seWorld = latLng2World(se); - const dx = nwWorld.x < seWorld.x - ? seWorld.x - nwWorld.x - : (1 - nwWorld.x) + seWorld.x; - const dy = seWorld.y - nwWorld.y; +function fitNwSe(nw, se, width, height) { + const EPS = 0.000000001; + const nwWorld = latLng2World(nw); + const seWorld = latLng2World(se); + const dx = nwWorld.x < seWorld.x + ? seWorld.x - nwWorld.x + : (1 - nwWorld.x) + seWorld.x; + const dy = seWorld.y - nwWorld.y; - if (dx <= 0 && dy <= 0) { - return null; + if (dx <= 0 && dy <= 0) { + return null; + } + + const zoomX = log2(width / GOOGLE_TILE_SIZE / dx); + const zoomY = log2(height / GOOGLE_TILE_SIZE / dy); + const zoom = Math.floor(EPS + Math.min(zoomX, zoomY)); + + // TODO find center just unproject middle world point + const middle = { + x: nwWorld.x < seWorld.x // eslint-disable-line + ? 0.5 * (nwWorld.x + seWorld.x) + : nwWorld.x + seWorld.x - 1 > 0 + ? 0.5 * (nwWorld.x + seWorld.x - 1) + : 0.5 * (1 + nwWorld.x + seWorld.x), + y: 0.5 * (nwWorld.y + seWorld.y), + }; + + const scale = Math.pow(2, zoom); + const halfW = width / scale / GOOGLE_TILE_SIZE / 2; + const halfH = height / scale / GOOGLE_TILE_SIZE / 2; + + const newNW = world2LatLng({ + x: middle.x - halfW, + y: middle.y - halfH, + }); + + const newSE = world2LatLng({ + x: middle.x + halfW, + y: middle.y + halfH, + }); + + return { + center: world2LatLng(middle), + zoom, + newBounds: { + nw: newNW, + se: newSE, + }, + }; +} + +export function convertNeSwToNwSe({ ne, sw }) { + return { + nw: { + lat: ne.lat, + lng: sw.lng, + }, + se: { + lat: sw.lat, + lng: ne.lng, + }, + }; +} + +export function convertNwSeToNeSw({ nw, se }) { + return { + ne: { + lat: nw.lat, + lng: se.lng, + }, + sw: { + lat: se.lat, + lng: nw.lng, + }, + }; +} + +export function fitBounds({ nw, se, ne, sw }, { width, height }) { + let fittedData; + + if (nw && se) { + fittedData = fitNwSe(nw, se, width, height); + } else { + const calculatedNwSe = convertNeSwToNwSe({ ne, sw }); + fittedData = fitNwSe(calculatedNwSe.nw, calculatedNwSe.se, width, height); + } + + return { + ...fittedData, + newBounds: { + ...fittedData.newBounds, + ...convertNwSeToNeSw(fittedData.newBounds), + }, + }; +} + +// ------------------------------------------------------------------- +// Helpers to calc some markers size + +export function meters2ScreenPixels(meters, { lat, lng }, zoom) { + const { w, h } = meters2WorldSize(meters, { lat, lng }); + const scale = Math.pow(2, zoom); + const wScreen = w * scale * GOOGLE_TILE_SIZE; + const hScreen = h * scale * GOOGLE_TILE_SIZE; + return { + w: wScreen, + h: hScreen, + }; +} + +// -------------------------------------------------- +// Helper functions for working with svg tiles, (examples coming soon) + +export function tile2LatLng({ x, y }, zoom) { + const n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom); + + return ({ + lat: (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))), + lng: (x / Math.pow(2, zoom) * 360 - 180), + }); +} + +export function latLng2Tile({ lat, lng }, zoom) { + const worldCoords = latLng2World({ lat, lng }); + const scale = Math.pow(2, zoom); + + return { + x: Math.floor(worldCoords.x * scale), + y: Math.floor(worldCoords.y * scale), + }; +} + +export function getTilesIds({ from, to }, zoom) { + const scale = Math.pow(2, zoom); + + const ids = []; + for (let x = from.x; x !== (to.x + 1) % scale; x = (x + 1) % scale) { + for (let y = from.y; y !== (to.y + 1) % scale; y = (y + 1) % scale) { + ids.push([zoom, x, y]); } + } - const zoomX = log2(width / GOOGLE_TILE_SIZE / dx); - const zoomY = log2(height / GOOGLE_TILE_SIZE / dy); - const zoom = Math.floor(EPS + Math.min(zoomX, zoomY)); - - // TODO find center just unproject middle world point - const middle = { - x: nwWorld.x < seWorld.x // eslint-disable-line - ? 0.5 * (nwWorld.x + seWorld.x) - : nwWorld.x + seWorld.x - 1 > 0 - ? 0.5 * (nwWorld.x + seWorld.x - 1) - : 0.5 * (1 + nwWorld.x + seWorld.x), - y: 0.5 * (nwWorld.y + seWorld.y), - }; - - const scale = Math.pow(2, zoom); - const halfW = width / scale / GOOGLE_TILE_SIZE / 2; - const halfH = height / scale / GOOGLE_TILE_SIZE / 2; - - const newNW = world2LatLng({ - x: middle.x - halfW, - y: middle.y - halfH, - }); - - const newSE = world2LatLng({ - x: middle.x + halfW, - y: middle.y + halfH, - }); - - return { - center: world2LatLng(middle), - zoom, - newBounds: { - nw: newNW, - se: newSE, - }, - }; - }, - - // ------------------------------------------------------------------- - // Helpers to calc some markers size - - meters2ScreenPixels(meters, { lat, lng }, zoom) { - const { w, h } = meters2WorldSize(meters, { lat, lng }); - const scale = Math.pow(2, zoom); - const wScreen = w * scale * GOOGLE_TILE_SIZE; - const hScreen = h * scale * GOOGLE_TILE_SIZE; - return { - w: wScreen, - h: hScreen, - }; - }, - - // -------------------------------------------------- - // Helper functions for working with svg tiles, (examples coming soon) - - tile2LatLng({ x, y }, zoom) { - const n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom); - - return ({ - lat: (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))), - lng: (x / Math.pow(2, zoom) * 360 - 180), - }); - }, - - latLng2Tile({ lat, lng }, zoom) { - const worldCoords = latLng2World({ lat, lng }); - const scale = Math.pow(2, zoom); - - return { - x: Math.floor(worldCoords.x * scale), - y: Math.floor(worldCoords.y * scale), - }; - }, - - getTilesIds({ from, to }, zoom) { - const scale = Math.pow(2, zoom); - - const ids = []; - for (let x = from.x; x !== (to.x + 1) % scale; x = (x + 1) % scale) { - for (let y = from.y; y !== (to.y + 1) % scale; y = (y + 1) % scale) { - ids.push([zoom, x, y]); - } - } - - return ids; - }, -}; - -export const fitBounds = exports.fitBounds; -export const meters2ScreenPixels = exports.meters2ScreenPixels; -export const tile2LatLng = exports.tile2LatLng; -export const latLng2Tile = exports.latLng2Tile; -export const getTilesIds = exports.getTilesIds; -// export default exports; + return ids; +}