JavaScript get map visible area

3439
7
Jump to solution
01-09-2021 09:59 AM
AmitKahn1
New Contributor II

Hi,

I am looking for JavaScript API (js.arcgis.com/4.15) that enables to get the map visible area.

Hence, the X,Y coordinates of the visible area (polygon or rect coordiantes) of the presented map.

In android arcgis SDK there is method getVisibleArea() that does it

Google Map api provides it as:

var visibleRegion = googleMap.projection
visibleRegion.farRight.latitude, visibleRegion.farRight.longitude, visibleRegion.nearLeft.latitude, visibleRegion.nearLeft.longitude

I've made some expirimates retrieve it from 

let view = new MapView({
container: "viewDiv",
map: map,
zoom: 7,
center: [-117.295800, 34.076295], // longitude, latitude, centered on Austria
constraints: {
minZoom: 16 // Use this constraint to avoid zooming out too far
}
});
view.when(function() { 
let initialExtent = view.extent;
let height = initialExtent.height;
let mmax = initialExtent.mmax;
let mmin = initialExtent.mmin;
let type = initialExtent.type;
let width = initialExtent.width;
let xmax = initialExtent.xmax;
let xmin = initialExtent.xmin;
let ymax = initialExtent.ymax;
let ymin = initialExtent.ymin;
}

However the numbers I get are not coordinates at all:

xmax: -13055474.239510546
xmin: -13059143.216868332
ymax: 4039950.5164796906
ymax: 4039950.5164796906
ymin: 4038151.857657808

Also, try to convert it to Polygon doesn't  help:

var polygon = Polygon.fromExtent(initialExtent);

The polygon ring attribute contains the exact same values as above ... so it doesn't help at all. 

I have no idea how they can be 
converted to the top right/left/top/bottom coordinates of the presented map. Maybe this is not the correct approach, however this is as far as I could get. 

I've looked all over in MapView for getVisibleArea()  and couldn't find,

Thanks.

2 Solutions

Accepted Solutions
kerenc
by
New Contributor II

BTW , in order to change the extent to WGS84 it is much easier to use webMercatorUtils  and there's no need to load the projections.

View solution in original post

AmitKahn1
New Contributor II

Dear @kerenc ,

Thank you so much for the solution. For the sake of other who straggle the exact same, here is the complete code below.

Best,

Gabriel

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS API for JavaScript Tutorials: Create a JavaScript starter app</title>
<style>
html, body, #viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/themes/light/main.css">
<script src="https://js.arcgis.com/4.15/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/GraphicsLayer",
"esri/Graphic",
"esri/geometry/Polygon"
], function (Map, MapView, GraphicsLayer, Graphic, Polygon) {

var map = new Map({
basemap: "topo-vector"
});

var mapView = new MapView({
container: "viewDiv",
map: map,
center: [-117.295800, 34.076295], // longitude, latitude
zoom: 16
});

var graphicsLayer = new GraphicsLayer();

const point = {type: "point", longitude: -117.295800, latitude: 34.076295};
const pointPicture = {type: "point", longitude: -117.298800, latitude: 34.077295};

const simpleMarkerSymbol = {type: "simple-marker", color: [226, 119, 40]};


const pictureSymbol = {
type: "picture-marker", // autocasts as new PictureMarkerSymbol()
url: "https://static.arcgis.com/images/Symbols/Shapes/BlackStarLargeB.png",
width: "64px",
height: "64px"
};

var pointGraphic = new Graphic({geometry: point, symbol: simpleMarkerSymbol});
var pointPictureGraphic = new Graphic({geometry: pointPicture, symbol: pictureSymbol});

graphicsLayer.add(pointGraphic);
graphicsLayer.add(pointPictureGraphic);


map.add(graphicsLayer);

mapView.when(function(){
console.log("Map is ready")
}, function(error){
// This function will execute if the promise is rejected due to an error
});

mapView.watch('extent',getProjection)

function getProjection(camera) {
console.log("getProjection: started")
if(camera.extent.xmin == undefined)
return;

const initialExtent = mapView.extent;

const webMercatorUtils = require("esri/geometry/support/webMercatorUtils");
console.log("getProjection: webMercatorUtils = "+webMercatorUtils)
const nePoint = webMercatorUtils.xyToLngLat(initialExtent.xmin, initialExtent.ymin)
const swPoint = webMercatorUtils.xyToLngLat(initialExtent.xmax, initialExtent.ymax)

console.log("Map projection north east point is: "+nePoint)
console.log("Map projection south west point is: "+swPoint)
}
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html> 

 

View solution in original post

0 Kudos
7 Replies
VictorBerchet
Occasional Contributor

You can use a projection to convert to coordinates to lat/lon.

You want the convert to WGS84 to get lat/lon. Use WGS84 to get an instance of that projection.

(Check the "see also" links on the projection page for the details) 

0 Kudos
AmitKahn1
New Contributor II

Thank you for your quick response 🙂

I wish to validate I well understood the solution:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Limit MapView to Certain Area - ArcGIS JavaScript 4.15</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/css/main.css">
<script src="https://js.arcgis.com/4.15/"></script>
<style>
html, body, #viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<script>
require([
"esri/Map",
"esri/views/MapView"
], function(Map, MapView) {

let map = new Map({
basemap: "streets"
});

let view = new MapView({
container: "viewDiv",
map: map,
zoom: 7,
center: [-117.295800, 34.076295], // longitude, latitude, centered on Austria
constraints: {
minZoom: 16 // Use this constraint to avoid zooming out too far
}
});

view.when(function() {
getProjection(view);
});

function getProjection(view) {
const Projection = require("esri/geometry/projection");
const SpatialReference = require("esri/geometry/SpatialReference");

const initialExtent = view.extent;
const geogtrans = Projection.getTransformations(SpatialReference.WGS84, SpatialReference.WGS84, initialExtent);

geogtrans.forEach(function(geogtran, index) {
geogtran.steps.forEach(function(step, index) {
console.log("step wkid: ", step.wkid);
});
});
}
});


</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>

I have runtime error:
Uncaught (in promise) TypeError: Cannot read property '_getTransformationBySuitability' of null
at Object.a.getTransformations (mapViewDeps.js:45)
at getProjection (sample.html?_ijt=feaunen6peg1167143od9in19e:46)

It doesn't understand how to handle Projection. Projection type doesn't have a constructor as I could see.

Also, I am not sure I understand how to extract the latitudes and longitudes from the geogtrans variable.

@VictorBerchet can you please assist in fix my sample above and point were Lon and Lat within the geogtrans? 

 

Thank you very much.

0 Kudos
kerenc
by
New Contributor II
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>Limit MapView to Certain Area - ArcGIS JavaScript 4.15</title>
    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.18/esri/css/main.css"
    />
    <script src="https://js.arcgis.com/4.18/"></script>
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
    </style>
    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/geometry/projection",
        "esri/geometry/SpatialReference"
      ], function (Map, MapView, Projection, SpatialReference) {
        let map = new Map({
          basemap: "streets"
        });

        let view = new MapView({
          container: "viewDiv",
          map: map,
          zoom: 7,
          center: [-117.2958, 34.076295], // longitude, latitude, centered on Austria
          constraints: {
            minZoom: 16 // Use this constraint to avoid zooming out too far
          }
        });

        //view.when(function () {
        //  getProjection(view);
        //});
        
        view.watch('extent',getProjection)

        function getProjection(camera) {
          
          if(camera.extent.xmin == undefined)
            return;
          const projection = require("esri/geometry/projection");
          const SpatialReference = require("esri/geometry/SpatialReference");
          //projection = new Projection();
          projection.load().then(function() {
            const initialExtent = view.extent;
            console.log("projection.load()");
            debugger;
            projection.getTransformations(
              SpatialReference.WGS84,
              new SpatialReference(2039),
              initialExtent
            ).then(function(geogtrans){
  
            geogtrans.forEach(function (geogtran, index) {
              geogtran.steps.forEach(function (step, index) {
                console.log("step wkid: ", step.wkid);
              });
            });
          })
        
        });
      }
      });
    </script>
  </head>
  <body>
    <div id="viewDiv"></div>
  </body>
</html>

I tried to do minimal changes to  your code to make it work.

Notice that I've added the load method to projection class and I watch the extent in order to use it only after it is populated with value (better change the code to change it once or skip the load when it is loaded).

Any way it has no logic to get transformations from 2 exact same projections so that it returns an empty array on my sample code - try to use to different projections...

Good luck

 

0 Kudos
AmitKahn1
New Contributor II

Thank you @kerenc for your assistance. 

Code fails during runtime:

projection.load()
esri.html?_ijt=b7g5ecd5goqr8hnarkbpa2atn5:66 Uncaught (in promise) TypeError: projection.getTransformations(...).then is not a function
at esri.html?_ijt=b7g5ecd5goqr8hnarkbpa2atn5:66

Screen Shot 2021-01-10 at 18.04.49.png

Can you please advise how to fix?

 

Also, I am not fixed with specific method for getting map projection. Also tried GeometryService that failed since require("esri/tasks/GeometryService") gives Error: undefinedModule ... however here is the code:

require([
"esri/Map",
"esri/views/MapView",
"esri/geometry/projection",
"esri/geometry/SpatialReference"
], function (Map, MapView, Projection, SpatialReference) {
let map = new Map({
basemap: "streets"
});

let view = new MapView({
container: "viewDiv",
map: map,
zoom: 7,
center: [-117.2958, 34.076295], // longitude, latitude, centered on Austria
constraints: {
minZoom: 16 // Use this constraint to avoid zooming out too far
}
});

view.watch('extent',getProjection)

function getProjection(camera) {

if(camera.extent.xmin == undefined)
return;

const initialExtent = view.extent;
const Point = require("esri/geometry/Point");
const GeometryService = require("esri/tasks/GeometryService");
const ProjectParameters = require("esri/tasks/support/ProjectParameters");
var pointTopRight = new Point(initialExtent.xmin, initialExtent.ymin, map.spatialReference );
var pointBottomLeft = new Point(initialExtent.xmax, initialExtent.ymax, map.spatialReference );
var gsvc = new GeometryService("https://utility.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
var params = new ProjectParameters();

params.geometries = [pointTopRight, pointBottomLeft];
params.outSR = map.spatialReference;
gsvc.project(params, function(results){
console.log(results)
});
}
});

(index):32 Uncaught Error: undefinedModule
at n ((index):32)
at Z ((index):39)
at d ((index):32)
at getProjection (esri.html?_ijt=b7g5ecd5goqr8hnarkbpa2atn5:59) 

@kerenc As you could see I've tried to solve the problem from different angels, so far no success.

I thought that the question of getting the boudries coordinates (Lat, Lon)  of a map view should be fundamental. I hoped to find a straightforward way to get it.

Thanks for your kind help, any solution that can provide the mapView Lon/Lat visible area projection is most Wellcome. Looking forward your respone.

Best,

Gabriel

0 Kudos
kerenc
by
New Contributor II

You really do not need all those lines to get the visible area, you can watch the view's extent and get it.
Esri's help samples has is a nice sample that demonstrates it: https://developers.arcgis.com/javascript/latest/sample-code/watch-for-changes/index.html 

In order to get the spatial reference just read view.extent.spatialReference



Regarding your code fix, I apologize, At first glance, I thought that it failed because the method returns a promise and after reading the api reference I realized you were missing the call to projections.load(). 
I forgot to change it back.
Here is the fixed code:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>Limit MapView to Certain Area - ArcGIS JavaScript 4.15</title>
    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.15/esri/css/main.css"
    />
    <script src="https://js.arcgis.com/4.15/"></script>
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
    </style>
    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/geometry/projection",
        "esri/geometry/SpatialReference"
      ], function (Map, MapView, Projection, SpatialReference) {
        let map = new Map({
          basemap: "streets"
        });

        let view = new MapView({
          container: "viewDiv",
          map: map,
          zoom: 7,
          center: [-117.2958, 34.076295], // longitude, latitude, centered on Austria
          constraints: {
            minZoom: 16 // Use this constraint to avoid zooming out too far
          }
        });

        //view.when(function () {
        //  getProjection(view);
        //});

        view.watch("extent", getProjection);

        function getProjection(camera) {
          if (camera.extent.xmin == undefined) return;
          const projection = require("esri/geometry/projection");
          const SpatialReference = require("esri/geometry/SpatialReference");
          //projection = new Projection();
          projection.load().then(function () {
            const initialExtent = view.extent;
            console.log("projection.load()");
            
            geogtrans = projection
              .getTransformations(
                SpatialReference.WGS84,
                new SpatialReference(3636),
                initialExtent
              )
             
                geogtrans.forEach(function (geogtran, index) {
                  geogtran.steps.forEach(function (step, index) {
                    console.log("step wkid: ", step.wkid);
                  });
             
              });
          });
        }
      });
    </script>
  </head>
  <body>
    <div id="viewDiv"></div>
  </body>
</html>
0 Kudos
kerenc
by
New Contributor II

BTW , in order to change the extent to WGS84 it is much easier to use webMercatorUtils  and there's no need to load the projections.

AmitKahn1
New Contributor II

Dear @kerenc ,

Thank you so much for the solution. For the sake of other who straggle the exact same, here is the complete code below.

Best,

Gabriel

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS API for JavaScript Tutorials: Create a JavaScript starter app</title>
<style>
html, body, #viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/themes/light/main.css">
<script src="https://js.arcgis.com/4.15/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/GraphicsLayer",
"esri/Graphic",
"esri/geometry/Polygon"
], function (Map, MapView, GraphicsLayer, Graphic, Polygon) {

var map = new Map({
basemap: "topo-vector"
});

var mapView = new MapView({
container: "viewDiv",
map: map,
center: [-117.295800, 34.076295], // longitude, latitude
zoom: 16
});

var graphicsLayer = new GraphicsLayer();

const point = {type: "point", longitude: -117.295800, latitude: 34.076295};
const pointPicture = {type: "point", longitude: -117.298800, latitude: 34.077295};

const simpleMarkerSymbol = {type: "simple-marker", color: [226, 119, 40]};


const pictureSymbol = {
type: "picture-marker", // autocasts as new PictureMarkerSymbol()
url: "https://static.arcgis.com/images/Symbols/Shapes/BlackStarLargeB.png",
width: "64px",
height: "64px"
};

var pointGraphic = new Graphic({geometry: point, symbol: simpleMarkerSymbol});
var pointPictureGraphic = new Graphic({geometry: pointPicture, symbol: pictureSymbol});

graphicsLayer.add(pointGraphic);
graphicsLayer.add(pointPictureGraphic);


map.add(graphicsLayer);

mapView.when(function(){
console.log("Map is ready")
}, function(error){
// This function will execute if the promise is rejected due to an error
});

mapView.watch('extent',getProjection)

function getProjection(camera) {
console.log("getProjection: started")
if(camera.extent.xmin == undefined)
return;

const initialExtent = mapView.extent;

const webMercatorUtils = require("esri/geometry/support/webMercatorUtils");
console.log("getProjection: webMercatorUtils = "+webMercatorUtils)
const nePoint = webMercatorUtils.xyToLngLat(initialExtent.xmin, initialExtent.ymin)
const swPoint = webMercatorUtils.xyToLngLat(initialExtent.xmax, initialExtent.ymax)

console.log("Map projection north east point is: "+nePoint)
console.log("Map projection south west point is: "+swPoint)
}
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html> 

 

0 Kudos