Maps: setBounds zooms to incorrect location when over antemeridian

Created on 14 May 2020  路  5Comments  路  Source: react-native-mapbox-gl/maps

If you call setting bounds on the camera with coordinates crossing the antemeridian (eg { ne: [150, 20], sw: [-179, -20] } it zooms to the map as if the bounds had west and east swapped (eg { ne: [-179, 20], sw: [150, -20] }`. Instead it should zoom the map to the area crossing the antemeridian (line at +-180 lon).

To Reproduce
use the starter instructions, and the following map code

          <MapboxGL.MapView style={styles.map} >
            <MapboxGL.Camera bounds={{
              ne: [150, 20],
              sw: [-179, -20],
            }}/>
            </MapboxGL.MapView>

this zooms here:
Simulator Screen Shot - iPhone 11 - 2020-05-15 at 09 19 27

Expected behavior
it should zoom to a region in the pacific eg
Simulator Screen Shot - iPhone 11 - 2020-05-15 at 09 20 08

Versions (please complete the following information):

  • Platform: both
  • Device: iPhone X
  • Emulator/ Simulator: yes
  • OS: iOS 13.4.1
  • react-native-mapbox-gl Version [e.g. 7.0.9]
  • React Native Version 0.62.2

This could be a problem inside of mapbox itself, im not too sure how the camera/zooming works

bug help wanted wontfix

Most helpful comment

In MapboxGL it's implemented with setVisibleCoordinatesBounds. Just add 360 to the Western Longitude.

e.g. to show Japan and Mexico:
LongitudeNE: 128.43
LongitudeSW: 273.19

So you just need a React Native Wrapper for that method. I did a quick and dirty version with custom data types for my project, but have a look for some inspiration: https://github.com/jaysquared/maps/commit/7d369a77d6bd5d8cafdda6d776b81e982f4d48a5

To calculate out of an array of Coordinates / CoordinateBounds I use this method:

const generateVisibleBounds = (mixedBounds: Array<CoordinateBoundsType | CoordinateType>): ?CoordinateBoundsType => {

  const latitudes = [];
  const longitudes = [];
  mixedBounds.forEach((mixedBound) => {
    if (typeof mixedBound.latitude === 'number' && typeof mixedBound.longitude === 'number') {
      latitudes.push(mixedBound.latitude);
      longitudes.push(mixedBound.longitude);
    } else {
      latitudes.push(mixedBound.sw.latitude);
      longitudes.push(mixedBound.sw.longitude);
      latitudes.push(mixedBound.ne.latitude);
      longitudes.push(mixedBound.ne.longitude);
    }
  });

  const latitudeNE = Math.max(...latitudes);
  const latitudeSW = Math.min(...latitudes);
  let longitudeNE;
  let longitudeSW;

  const eastLongitudes = longitudes.filter(longitude => longitude >= 0);
  const westLongitudes = longitudes.filter(longitude => longitude < 0);

  if (eastLongitudes.length === 0 || westLongitudes.length === 0) {
    longitudeNE = Math.max(...longitudes);
    longitudeSW = Math.min(...longitudes);
  } else {
    console.log({ eastLongitudes, westLongitudes });
    const minEastLongitude = Math.min(...eastLongitudes);
    const maxEastLongitude = Math.max(...eastLongitudes);
    const minWestLongitude = Math.min(...westLongitudes);
    const maxWestLongitude = Math.max(...westLongitudes);

    const longitudeSpreadDefault = maxEastLongitude - minWestLongitude;
    const longitudeSpreadDateline = 360 - minEastLongitude + maxWestLongitude;

    if (longitudeSpreadDateline < longitudeSpreadDefault) {
      longitudeNE = minEastLongitude;
      longitudeSW = 360 + maxWestLongitude;
    } else {
      longitudeNE = maxEastLongitude;
      longitudeSW = minWestLongitude;
    }
  }


  if (!latitudeNE || !latitudeSW || !longitudeNE || !longitudeSW) {
    return null;
  }
  return {
    sw: {
      latitude: latitudeSW,
      longitude: longitudeSW,
    },
    ne: {
      latitude: latitudeNE,
      longitude: longitudeNE,
    },
  };
};

All 5 comments

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

any updates?

Reopening this, as it's a legitimate issue

In MapboxGL it's implemented with setVisibleCoordinatesBounds. Just add 360 to the Western Longitude.

e.g. to show Japan and Mexico:
LongitudeNE: 128.43
LongitudeSW: 273.19

So you just need a React Native Wrapper for that method. I did a quick and dirty version with custom data types for my project, but have a look for some inspiration: https://github.com/jaysquared/maps/commit/7d369a77d6bd5d8cafdda6d776b81e982f4d48a5

To calculate out of an array of Coordinates / CoordinateBounds I use this method:

const generateVisibleBounds = (mixedBounds: Array<CoordinateBoundsType | CoordinateType>): ?CoordinateBoundsType => {

  const latitudes = [];
  const longitudes = [];
  mixedBounds.forEach((mixedBound) => {
    if (typeof mixedBound.latitude === 'number' && typeof mixedBound.longitude === 'number') {
      latitudes.push(mixedBound.latitude);
      longitudes.push(mixedBound.longitude);
    } else {
      latitudes.push(mixedBound.sw.latitude);
      longitudes.push(mixedBound.sw.longitude);
      latitudes.push(mixedBound.ne.latitude);
      longitudes.push(mixedBound.ne.longitude);
    }
  });

  const latitudeNE = Math.max(...latitudes);
  const latitudeSW = Math.min(...latitudes);
  let longitudeNE;
  let longitudeSW;

  const eastLongitudes = longitudes.filter(longitude => longitude >= 0);
  const westLongitudes = longitudes.filter(longitude => longitude < 0);

  if (eastLongitudes.length === 0 || westLongitudes.length === 0) {
    longitudeNE = Math.max(...longitudes);
    longitudeSW = Math.min(...longitudes);
  } else {
    console.log({ eastLongitudes, westLongitudes });
    const minEastLongitude = Math.min(...eastLongitudes);
    const maxEastLongitude = Math.max(...eastLongitudes);
    const minWestLongitude = Math.min(...westLongitudes);
    const maxWestLongitude = Math.max(...westLongitudes);

    const longitudeSpreadDefault = maxEastLongitude - minWestLongitude;
    const longitudeSpreadDateline = 360 - minEastLongitude + maxWestLongitude;

    if (longitudeSpreadDateline < longitudeSpreadDefault) {
      longitudeNE = minEastLongitude;
      longitudeSW = 360 + maxWestLongitude;
    } else {
      longitudeNE = maxEastLongitude;
      longitudeSW = minWestLongitude;
    }
  }


  if (!latitudeNE || !latitudeSW || !longitudeNE || !longitudeSW) {
    return null;
  }
  return {
    sw: {
      latitude: latitudeSW,
      longitude: longitudeSW,
    },
    ne: {
      latitude: latitudeNE,
      longitude: longitudeNE,
    },
  };
};

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

atomheartother picture atomheartother  路  3Comments

MariaSyed picture MariaSyed  路  4Comments

dorthwein picture dorthwein  路  3Comments

magnusburton picture magnusburton  路  3Comments

loic-lopez picture loic-lopez  路  6Comments