GithubHelp home page GithubHelp logo

mapbox / geo-viewport Goto Github PK

View Code? Open in Web Editor NEW
190.0 110.0 40.0 404 KB

Turns bounding boxes / extents into centerpoint & zoom combos for static maps.

License: BSD 2-Clause "Simplified" License

HTML 14.08% JavaScript 85.92%

geo-viewport's Introduction

Build Status codecov

geo-viewport

Turns bounding boxes / extents into centerpoint & zoom combos for static maps.

Works in node.js and browsers, via browserify or a script tag.

Install

npm install --save @mapbox/geo-viewport

Or use a plugin:

<script src='//api.tiles.mapbox.com/mapbox.js/plugins/geo-viewport/v0.2.1/geo-viewport.js'></script>

The script-tag include exports an object called geoViewport, with methods bounds and viewport documented below.

Example

Live example with Mapbox Static Map API

With Node

var geoViewport = require('@mapbox/geo-viewport');

geoViewport.viewport([
    5.668343999999995,
    45.111511000000014,
    5.852471999999996,
    45.26800200000002
], [640, 480])

// yields
// {
//     center: [
//         5.7604079999999955,
//         45.189756500000016
//     ],
//     zoom: 11
// }

In a browser:

<script src='//api.tiles.mapbox.com/mapbox.js/plugins/geo-viewport/v0.1.1/geo-viewport.js'></script>
<script>
var bounds = geoViewport.viewport([
    5.668343999999995,
    45.111511000000014,
    5.852471999999996,
    45.26800200000002
], [640, 480]);

var center = geoViewport.bounds(
  [-75.03,
  35.25],
  14,
  [600, 400]);

console.log(bounds);
console.log(center);
</script>

api

viewport(bounds, dimensions, minzoom, maxzoom, tileSize, allowFloat, allowAntiMeridian)

Given a WSEN array of bounds and a [x, y] array of pixel dimensions, return a { center: [lon, lat], zoom: zoom } viewport. Use allowFloat to retain float values in the output.

Example:

// first argument is the bounds, and the image is 640x480
geoViewport.viewport([
    5.6683, 45.111, 5.8524, 45.268
], [640, 480])

bounds(viewport, zoom, dimensions, tileSize)

Given a centerpoint as [lon, lat] or { lon, lat }, a zoom, and dimensions as [x, y], return a bounding box.

Example:

geoViewport.bounds([-75.03, 35.25], 14, [600, 400])

tile sizes

Be aware that these calculations are sensitive to tile size. The default size assumed by this library is 256x256px; however, Mapbox Vector Tiles are 512x512px.

For example, to calculating a bounding box for a classic raster-based 256x256 tile:

geoViewport.bounds([-75.03, 35.25], 14, [600, 400], 256)

// since 256 is default, you can omit the tileSize param
geoViewport.bounds([-75.03, 35.25], 14, [600, 400])

To calculate a bounding box for a Mapbox vector tile source, such as an image from the Mapbox Static Image API:

geoViewport.bounds([-75.03, 35.25], 14, [600, 400], 512)

There's a handy blog post discussing the issue here.

geo-viewport's People

Contributors

flippmoke avatar jingsam avatar mapsam avatar samanpwbb avatar teasealancs avatar timiyay avatar tmcw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

geo-viewport's Issues

viewport and bounds don't return the same values

Hello, I'm working with this library in order to compose a big image with a matrix of 3x4 images. I compute precisely the bounds of each images to retrieved them through the Static API. I am not be able to correctly connect the dots between each images, because I think that the zoom level I have from viewport() is slightly lower than expected. I also found that reverse the viewport using bound() doesn't give my my initial bounds. This is a problem when you try to be extremely precise.

const viewport = geoViewport.viewport([
    -5.715601797359881,
    51.267265845221885,
    -2.2190913285227603,
    53.69793118651373
      ], [1169, 1240], undefined, undefined, 512, true)

// { center: [-3.9673465629413203, 52.48259851586781], zoom: 7.770670445470639 }

const bounds = geoViewport.bounds([viewport.center[0], viewport.center[1]], viewport.zoom, [1169, 1240], 512)

// [-5.8493142602338875, 51.250005339825485, -2.0853788656487575, 53.681598862577374]

I played with the mapbox-gl-js in a JSFiddle here I found that the zoom level is more 7.816474199832816.

Did I missed something?

Zoom is always NaN

@tmcw I'm having issues getting a zoomlevel. The center is great, but I keep on getting NaN for the zoomlevel. Am I doing anything wrong?

var geojsonExtent = require('geojson-extent');
var geoViewport = require('geo-viewport');

// Example geojson: http://geojson.io/#id=gist:bsudekum/5e078af8113dda68ce53c659401178c5&map=7/51.522/-2.054
var geojson = {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[0.142822265625,51.957807388715516],[-1.549072265625,51.951036645095904],[-1.549072265625,51.19999983412068],[-1.131591796875,50.812877010308966],[0.3076171875,50.812877010308966],[0.37353515625,50.972264889367494],[0.560302734375,51.65211086156918],[0.142822265625,51.957807388715516]]]}}]}
var ext = geojsonExtent(geojson);
var vp = geoViewport.viewport(ext, 200, 200);

console.log(vp); //{ center: [ -0.494384765625, 51.38534219951224 ], zoom: NaN }

Center point calculation of the viewbox seems off

The source code has the center point calculation as:

center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2],

I think operations on y axis isn't amenable to planar arithmetic as they are in spherical mercator coordinates and one has to do a projection to pixel coordinates first before performing the midpoint algorithm.

Wrong zoom level or NaN zoom

Hello, I am using this library to generate multiple mapbox static map with differents bounds.
I have 4 generated maps and 3 of them have incorrect zoom:

the good one :

const viewport =  geoViewport.viewport([40.7127281,-74.0060152,48.862725,2.287592],[877,1240],0,20,512);
//  { center: [ 44.78772655, -35.859211599999995 ], zoom: 2 }

result =>
IcN2PsQ2S

One of the 4 generated maps gives me a wrong zoom level :

const viewport = geoViewport.viewport([36.1672559,-122.419,48.862725,2.287592],[877,1240],0,20,512);

//  { center: [ 42.51499045, -60.065704 ], zoom: 3 }

I should have bounds between San-Francisco and Paris but it gives me this =>
jqCt596g

(The center coordinates are good but zoom level isn't)

And the 2 others viewport gives me "NaN" zoom:

const viewport = geoViewport.viewport([36.1672559,-122.419,37.7793,-115.1485163],[877,1240],0,20,512);

//  { center: [ 36.973277949999996, -118.78375815 ], zoom: NaN }

and

const viewport = geoViewport.viewport([37.7793,-122.419,40.7127281,-74.0060152],[877,1240],0,20,512);

//  { center: [ 39.24601405, -98.2125076 ], zoom: NaN }

I always use the same function in my code and WSEN array of bounds is always respected.
I am a little confused about what happened here =/

Did I missed something?

Thank you =)

How to declare right image size with bounds?

I want to get static image for bounds [102.4494852795,29.3165265128,105.5825147205,32.0114734872] via static image API and this viewport.

Viewport show me:

{center: [104.01599999999999, 30.664], zoom: 8}

But how to specific the images size that fits in the the bounds? I find that 100x100 is different from 1000x1000.

Is there any way to do this?

Ruby port

Is there any community or Mapbox interest in a Ruby port of this library, and the underlying node-sphericalmercator library?

I've already ported it to a Ruby project, including the existing test suites. If anyone is into it, I can publish it as a Rubygem. Ideally, it would be maintained by Mapbox, but it wouldn't kill me to maintain it myself (if this repo is still alive).

I've been using it in a JS client for deriving the geographic bounding box for static maps that I request from the Mapbox Static Image API. We're adding endpoints to our Ruby API that allow us to spatially-query data using the same geo bbox as the underlying static map.

geoViewport.bounds() returns bounds for wrong zoom level

version: 0.1.1

I am using geoViewport.bounds() to compute the bounding box of an image from the Mapbox Static Image API.

However, the bounding box returned by geoViewport.bounds() is much larger than the actual geographic extent of the Static Map API image.

var zoom = 17;
var imageWidth = 1080;
var imageHeight = 350;
var mapboxPublicKey = 'foo';

// Full JSON not shown - it's a FeatureCollection, containing a single polygon feature
var geojsonFeature = {...FeatureCollection..}.features[0];


var centroid = d3.geo.centroid(geojsonFeature);

// Compute the bounding box based on zoom, centroid, and image dimensions
var imageBbox = geoViewport.bounds(centroid, zoom, [imageWidth, imageHeight]);
var imageBboxFeature = turf.bboxPolygon(imageBbox);

// Construct the Mapbox Static Image API URL, using the same parameters
var staticImageUrl = `https://api.mapbox.com/styles/v1/my-account/cikn8jw2f00fxbgm1tjbpdwxl/static/${centroid[0]},${centroid[1]},${zoom},0.00,0.00/${imageWidth}x${imageHeight}?access_token=${mapboxPublicKey}`

// ...more code that renders the image and geojsonFeature

The image bounding box created by the above scenario should cover the following geographic extent (the static map API returns an image with the same extent):
expected_bbox_extent

Instead, the bounding box computed from geoViewport.bounds() covers a larger extent:
actual_bbox_extent

{
    "type": "Feature",
    "geometry": {
        "type": "Polygon",
        "coordinates": [
            [
                [
                    144.9606192111969,
                    -37.83434424376551
                ],
                [
                    144.97220635414124,
                    -37.83434424376551
                ],
                [
                    144.97220635414124,
                    -37.83137845901042
                ],
                [
                    144.9606192111969,
                    -37.83137845901042
                ],
                [
                    144.9606192111969,
                    -37.83434424376551
                ]
            ]
        ]
    },
    "properties": {}
}

Use Case

I am building a D3 map, which will load a background image from the Mapbox Static Image API, and overlay it with GeoJSON. To do this, I need to compute the bounding box of the static image, based on the parameters I used in the API request (centre point, zoom, image width, image height). Once I have this bounding box, I use it to scale and translate the d3 mercator projection, so the GeoJSON features align with the background map image.

Support floats for zoom levels

Currently, the bounds() requires an integer for zoom, otherwise it returns NaN for the bounding box coordinates.

Now that both MapboxGL and the Mapbox Static Image API support floats for zoom, it'd be useful if bounds() did too.

Since this relies on upstream changes in node-sphericalmercator, I have raised the issue there: mapbox/sphericalmercator#19

How to add padding

Mapbox JS lib allows padding to pass to fitBounds(): this.map.fitBounds(this.bounds, { padding: 30 }) I wanted to do the same for static map using this lib, but can't find any padding option on mention.

Output floats for zoom levels in viewport function

Similar to #5 but to add support for floats for the viewport() function instead of the bounds()function.

Given the new Static Map API supports zoom levels up to two decimal places, it would be great to have an option to yield such value by removing the Math.floor() in the return.

Unlike the bounds() function, this would not require upstream changes in SphericalMercator.

viewport.bounds returns illegal coordinate sets for large dimensions

bounds returns an illegal "top left" coordinate set (minX < -180) when the first member of the dimensions array passed in is larger than a certain threshold. This happens for valid viewport (centerpoint) coordinates.

We found this bug when a call to the mapbox geocoding API was returning an error: BBox minX value must be a number between -180 and 180. The minX value was less than -180, and was coming from the geo-viewport bounds function.

Example arguments that cause the bug: viewport.bounds([-98.5556199, 39.8097343], 3, [2892, 480], 512)
Bounds returned: [ -225.615234375, 21.861498734372567, 28.564453125, 54.1109429427243 ]

Failing test case available here: master...coopy:geo-viewport:master#diff-9d2f3f2a83cca94e2e2185c0173c9170a80fdd3428bdf1c6f580f9d23f2fbe01R133

Add a License

Is it possible for someone to choose and add a license?

Zoom is NaN when using rectangular bounds

When calling geoViewport.viewport([33.841629, -118.91905, 34.27888, -117.954399], [384, 318]);, I get the following results:

center: (2) [34.0602545, -118.4367245]
zoom: NaN

It seems to work fine when using a fixed value ([34.16811, -118.231895, 34.16811, -118.231895]), so I'm guessing I must be doing something wrong with the data I'm passing in.

Any ideas?

zoom: NaN

There is a bug in the zoom calculation.
For example:

geoViewport.viewport([-122.03041139, 37.3316045, -122.02734894, 37.33017311], [640, 480]);

will return:

{ center: [ -122.028880165, 37.330888805 ], zoom: NaN }

Thanks!

Test suite fails on NodeJS v8

I recently updated my fork of the repo, and ran the tests on NodeJS v8.11.2.

The test suite failed on bounds for 512px tiles, as the resulting float had an extra decimal place than expected:

 bounds for 512px tiles
  not ok should be equal
    +++ found
    --- wanted
    -38.89697827424865
    +38.896978274248625

The tests pass on NodeJS 6 and below.

Hosted 0.2.1 version not yet available

Trying to access <script src='//api.tiles.mapbox.com/mapbox.js/plugins/geo-viewport/v0.2.1/geo-viewport.js'></script> from the readme responds with {"message":"Not Found"}
v0.1.1 is fine though.

Incorrect zoom for some bounds

For some bounds it calculates incorrect zoom, for example here [-112.16601, 37.622623, -112.082016, 37.672085] it calculates zoom 13 and it doesn't include most of markers, correct zoom will be 12.

geoViewport.viewport([-112.16601, 37.622623, -112.082016, 37.672085], [672, 376])

Here is example URL for static map with zoom 13:
https://api.mapbox.com/styles/v1/tripexpertweb/ckcdpjifn06ru1impw321xsqi/static/pin-s+000(-112.15432,37.672085),pin-s+000(-112.082016,37.624809),pin-s+000(-112.16601,37.622623),pin-s+000(-112.15432,37.672085),pin-s+000(-112.15432,37.672085),pin-s+000(-112.15432,37.672085),pin-s+000(-112.15611,37.64101),pin-s+000(-112.15611,37.64101)/-112.12401330471039,37.64735841514176,13/672x376@2x?access_token=pk.eyJ1IjoidHJpcGV4cGVydHdlYiIsImEiOiJja2Fud3l2MDcwbXZ0MndyeGF3eGI3ZWhzIn0.zLqNa1RECyZZadF8zfQM5w

And correct one with zoom 12:
https://api.mapbox.com/styles/v1/tripexpertweb/ckcdpjifn06ru1impw321xsqi/static/pin-s+000(-112.15432,37.672085),pin-s+000(-112.082016,37.624809),pin-s+000(-112.16601,37.622623),pin-s+000(-112.15432,37.672085),pin-s+000(-112.15432,37.672085),pin-s+000(-112.15432,37.672085),pin-s+000(-112.15611,37.64101),pin-s+000(-112.15611,37.64101)/-112.12401330471039,37.64735841514176,12/672x376@2x?access_token=pk.eyJ1IjoidHJpcGV4cGVydHdlYiIsImEiOiJja2Fud3l2MDcwbXZ0MndyeGF3eGI3ZWhzIn0.zLqNa1RECyZZadF8zfQM5w

If I convert back to bounds from center and zoom, it doesn't match initial bounds:
geoViewport.bounds([-112.12401330471039, 37.64735841514176], 13, [672, 376])

[-112.1817398071289, 37.621845878167704, -112.0663833618164, 37.67295135774715]

In this case [2.269904, 48.849976, 2.391055, 48.874565] it calculates zoom 12 which is correct and includes all markers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.