I'm a newbie to arcgis. I'm trying to show a particular location at a particular angle in 3D. The location is the corner of Water Street and Fifth Avenue in McKeesport, Pa. The angle is facing east. The tilt is 45. But this is what I'm getting: https://codepen.io/lschneiderman/pen/ZEQEWXG
My code is below:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS 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>
</head>
<body>
<div id="viewDiv"></div>
<script>
require([
"esri/Map",
"esri/views/SceneView"
], function(Map, SceneView) {
var map = new Map({
basemap: "topo-vector",
ground: "world-elevation" // show elevation
});
var view = new SceneView({
container: "viewDiv",
map: map,
heading: 90,
camera: {
position: { // observation point
latitude: '40.350500',
longitude: '-79.868870',
z: 1000 // altitude in meters
},
tilt: 45 // perspective in degrees
}
});
});
</script>
</body>
</html>
Make sure you set the heading (angle) of the viewing direction on the camera object. This will make the camera look east, positioned above the crossing
var view = new SceneView({
container: "viewDiv",
map: map,
camera: {
position: {
// observation point
latitude: "40.350500",
longitude: "-79.868870",
z: 700 // altitude in meters// altitude in meters
},
heading: 90,
tilt: 45 // perspective in degrees
}
});
Here is a link to the modified CodePen: https://codepen.io/arnofiva/pen/688eee67141131712a12ccc3310a6ea4?editors=1010
If you want the camera to look at the crossing, you need to adopt the position by moving the camera east, e.g.:
latitude: "40.350464",
longitude: "-79.874628",
Here's another version of the CodePen that shows the crossing in the view field of the camera:
https://codepen.io/arnofiva/pen/a2da1acb4cba398721690bf4d0b2101a?editors=0010
Instead of setting these values manually, it might be easier to use SceneView.goTo() and pass the point you want it to focus on as an argument:
var view = new SceneView({
container: "viewDiv",
map: map
});
view.goTo({
target: [-79.86887, 40.3505], // coordinates of crossing
heading: 90,
tilt: 45,
zoom: 18 // instead of a z-value, we provide the zoom level
}, {
duration: 0 // tell view not to animate camera movement
});
Here's the CodePen showing the use of goTo(): https://codepen.io/arnofiva/pen/e91dd1b257a002a0c4d007d3724e039f?editors=1010
Related
I have the following code that is currently working. I have been looking all over and cannot find how to change the icon way from the tiny dot that defaults on the location. The code below is set to a specific address on page load.
I have tried PictureMarkerSymbol as you'll see I have that loaded into the function, but I'm not getting the right logic.
I figured this would be the easy part of using arcgis, but it's proving to be difficult.
thanks!
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<title>ArcGIS API for JavaScript</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.20/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.20/"></script>
<script>
require([
'esri/config',
'esri/Map',
'esri/views/MapView',
'esri/symbols/PictureMarkerSymbol',
'esri/widgets/Search',
'esri/layers/FeatureLayer',
], function (
esriConfig,
Map,
MapView,
PictureMarkerSymbol,
Search,
FeatureLayer
) {
esriConfig.apiKey =
'API-KEY';
const map = new Map({
basemap: 'arcgis-navigation',
});
const view = new MapView({
container: 'viewDiv',
map: map,
center: [-77.050636, 38.889248],
zoom: 13,
});
const search = new Search({
//Add Search widget
view: view,
});
view.ui.add(search, 'top-right'); //Add to the map
searchWidget = new Search({
view: view,
searchTerm: '43 Valley Oak Ct',
popupEnabled: false,
});
view.ui.add(searchWidget, 'bottom-right');
view.when(() => {
searchWidget.search();
});
});
</script>
It looks as the search widget resultGraphic is readonly, but it looks like you can specify a collection of SearchSource and provide it a symbol. You can almost use the default setting provided in an example under sources in documentation. Below is an example. You can use this tool for creating symbols.
<!--
To run this demo, you need to replace 'YOUR_API_KEY' with an API key from the ArcGIS Developer dashboard.
Sign up for a free account and get an API key.
https://developers.arcgis.com/documentation/mapping-apis-and-services/get-started/
-->
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Search widget with multiple sources | Sample | ArcGIS API for JavaScript 4.20</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.20/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.20/"></script>
<script>
require(["esri/Map", "esri/views/MapView", "esri/layers/FeatureLayer", "esri/widgets/Search", "esri/tasks/Locator", "esri/symbols/SimpleMarkerSymbol"], (
Map,
MapView,
FeatureLayer,
Search,
Locator,
SimpleMarkerSymbol,
) => {
const map = new Map({
basemap: "dark-gray-vector"
});
const view = new MapView({
container: "viewDiv",
map: map,
center: [-97, 38], // lon, lat
scale: 10000000
});
var marker = new SimpleMarkerSymbol({ color: [203, 52, 52, 0.93] });
const searchWidget = new Search({
view: view,
searchTerm: '43 Valley Oak Ct',
popupEnabled: false,
sources: [
{
locator: new Locator("//geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"),
singleLineFieldName: "SingleLine",
outFields: ["Addr_type", "Match_addr", "StAddr", "City"],
name: "ArcGIS World Geocoding Service",
placeholder: "Find address or place",
resultSymbol: marker,
}
]
});
searchWidget.viewModel.includeDefaultSources = false;
// Add the search widget to the top left corner of the view
view.ui.add(searchWidget, {
position: "top-right"
});
view.when(() => {
searchWidget.search();
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
I have to change the default icon on the Locate widget on arcGIS 4.18. The default icon class is, esri-icon-locate how can I change it to the class, 'esri-icon-navigation'?
I am going through the documentation,
https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Locate.html#iconClass
I have tried to use the property, 'iconClass'. But not reflecting in the map icon. Please find the code below,
var locateBtn = new Locate({
view: view,
// iconClass: '\ue666'
iconClass: 'esri-icon-navigation'
});
view.ui.add(locateBtn, {
position: "manual",
});
KER,
You actually right, does not work as expected. Setting iconClass should be the solution.
Funny fact if you check the default iconClass is actually esri-icon-north-navigation, which obviously in not.
Anyway, I am gonna give a dirty solution, just overlap the class you want,
view.when(_ => {
const n = document.getElementsByClassName("esri-icon-locate");
if (n && n.length === 1) {
n[0].classList += " esri-icon-navigation"
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" />
<title>Locate button | Sample | ArcGIS API for JavaScript 4.18</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.18/esri/themes/light/main.css" />
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<script src="https://js.arcgis.com/4.18/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/widgets/Locate"
], function (Map, MapView, Locate) {
var map = new Map({
basemap: "topo-vector"
});
var view = new MapView({
container: "viewDiv",
map: map,
center: [-56.049, 38.485, 78],
zoom: 3
});
var locateBtn = new Locate({
view: view
});
// Add the locate widget to the top left corner of the view
view.ui.add(locateBtn, {
position: "top-left"
});
view.when(_ => {
const n = document.getElementsByClassName("esri-icon-locate");
if (n && n.length === 1) {
n[0].classList += " esri-icon-navigation"
}
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
I'm using arcGIS SceneView in local viewing mode to display a WebMap. I'm trying to constrain the zoom level and bounds of an area so that the user can only see the US, Hawaii, and Alaska and cannot pan outside of this. I also need to constrain the zoom level because if the user zooms out too far the over-zoom the map and see untiled/unmapped space.
Are there any potential solves for this? I first thought that using the constraints property might solve it, but it appears the properties that can be fed to this are quite limited:
https://developers.arcgis.com/javascript/latest/api-reference/esri-views-SceneView.html#constraints
One way to achieve what I think you want is:
listen to view property changes,
check constraints,
act accordingly.
Take a look a the example I made for you. In it, I wait for the view to stop updating (updating property), then I check the constraints. If it is out of scale or out of the extent I reset the view. You probably want another action, I just made it simple.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>
View constraints
</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.17/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.17/"></script>
<script>
require([
"esri/Map",
"esri/views/SceneView",
"esri/geometry/Extent"
], function (Map, SceneView, Extent) {
const extent = Extent.fromJSON(
{
"spatialReference": {
"latestWkid":3857,
"wkid":102100
},
"xmin":-13119716.983985346,
"ymin":4024337.3961656773,
"xmax":-13096023.097830579,
"ymax":4030581.302795334
}
);
const MIN_SCALE = 12000;
const MAX_SCALE = 48000;
const map = new Map({
basemap: "topo-vector",
ground: "world-elevation"
});
const view = new SceneView({
container: "viewDiv",
map: map,
viewingMode: "local",
center: [-117.75, 33.99],
scale: 24000
});
function resetView() {
console.log('reset');
view.goTo(
{
center:[-117.75, 33.99],
scale: 24000
}
);
}
view.watch("updating", function (value) {
if (!value) {
if (
// out of scale
view.scale < MIN_SCALE ||
view.scale > MAX_SCALE ||
// out of extent
view.extent.xmin < extent.xmin ||
extent.xmax < view.extent.xmax ||
view.extent.ymin < extent.ymin ||
extent.ymax < view.extent.ymax
) {
resetView();
};
}
});
});
</script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
I am new to ESRI ArcGIS. We are considering as a replacement for Google Maps. In this post https://www.esri.com/arcgis-blog/products/js-api-arcgis/announcements/migrating-from-google-maps-javascript-api-to-arcgis-api-for-javascript/ they explain how easy it should be to migrate to ArcGIS, but it seems like beyond the examples they provide, I am stuck.
I loaded a bunch of markers in a map. I am trying to have the map pan and zoom to fit all markers, and I have been unable to find any code that does that.
On Google Maps I'd do something like this:
myMap.options.center = new google.maps.LatLng(0,0);
myMap.options.mapTypeId = google.maps.MapTypeId[myMap.options.mapTypeId];
myMap.bounds = new google.maps.LatLngBounds();
myMap.map = new google.maps.Map(document.getElementById('cat-map'), myMap.options);
google.maps.event.addListenerOnce(myMap.map, 'bounds_changed', function(event){
myMap.map.fitBounds(myMap.bounds);
myMap.map.panToBounds(myMap.bounds);
});
for (var i = 0; i < myMap.items.length; i++) {
var itemLatlng = new google.maps.LatLng(myMap.items[i].lat, myMap.items[i].lng);
myMap.bounds.extend(itemLatlng);
new google.maps.Marker({
position: itemLatlng,
map: myMap.map,
});
}
But I have been unable to find the equivalent on ESRI ArcGIS.
Anyone has some guidance?
You have the right idea. In ArcGIS API Extent class has union method that extend the caller to include the geometry extent pass as parameter. Because you are using Point you can't use union method, Point extent is null. But no worries you can achieve this just iterating the graphics and extending the extent yourself.
Look at this example I made for you using as base the link of your question,
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS API for JavaScript Hello World App</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#zoomBtn {
margin: .5rem;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/css/main.css">
<script src="https://js.arcgis.com/4.15/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
'esri/geometry/Extent'
], function (Map, MapView, Extent) {
const map = new Map({
basemap: "streets-navigation-vector"
});
const view = new MapView({
container: "viewDiv",
map: map,
zoom: 12,
center: {
latitude: 32.7353,
longitude: -117.1490
}
});
function popupContent (feature) {
const div = document.createElement("div");
div.innerHTML = `Address: ${feature.graphic.attributes.addrs} <br>` +
`<a href='${feature.graphic.attributes.url}'>Web</a>`;
return div;
}
function toGraphic(color, lon, lat, title, addrs, url) {
return {
symbol: {
type: "text",
color,
text: "\ue61d",
font: {
size: 30,
family: "CalciteWebCoreIcons"
}
},
geometry: {
type: "point",
longitude: lon,
latitude: lat
},
attributes: {
title,
addrs,
url
},
popupTemplate: {
title: '{title}',
outfields: ['*'],
content: popupContent
}
}
}
const graphics = [
toGraphic(
'gray',
-117.1560632,
32.727482,
'Automotive Museum',
'2080 Pan American Plaza, San Diego, CA 92101, United States',
'http://sdautomuseum.org/'
),
toGraphic(
'gray',
-117.1763293,
32.7136902,
'USS Midway Museum',
'910 N Harbor Dr, San Diego, CA 92101, United States',
'http://www.midway.org/'
),
toGraphic(
'blue',
-117.2284536,
32.7641112,
'SeaWorld',
'500 Sea World Dr, San Diego, CA 92109, United States',
'https://seaworld.com/san-diego'
),
toGraphic(
'green',
-117.1557741,
32.7360032,
'Zoo',
'2920 Zoo Dr, San Diego, CA 92101, United States',
'https://zoo.sandiegozoo.org/'
)
];
view.graphics.addMany(graphics);
function maxExtent(graphics) {
const e = graphics
.map(g => g.geometry)
.reduce(
(acc, geom) => (
{
xmin: Math.min(acc.xmin, geom.longitude),
ymin: Math.min(acc.ymin, geom.latitude),
xmax: Math.max(acc.xmax, geom.longitude),
ymax: Math.max(acc.ymax, geom.latitude)
}
),
{
xmin: Number.MAX_SAFE_INTEGER,
ymin: Number.MAX_SAFE_INTEGER,
xmax: Number.MIN_SAFE_INTEGER,
ymax: Number.MIN_SAFE_INTEGER
}
);
return new Extent(e);
}
document.getElementById('zoomBtn')
.addEventListener(
'click',
_ => {
const ext = maxExtent(graphics);
console.log(`View Extent: ${JSON.stringify(view.extent)} Graphics Extent:${JSON.stringify(ext)}`);
view.extent = ext.expand(2); // expand a little so border points shows
}
);
});
</script>
</head>
<body>
<div class="esri-widget">
<button id="zoomBtn" class="esri-button">Zoom To Graphics</button>
</div>
<div id="viewDiv"></div>
</body>
</html>
Let's consider this code from the reference (https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Popup.html#open):
view.on("click", function(evt){
view.popup.open({
location: evt.mapPoint, // location of the click on the view
title: "Some title",
});
This works. But how to open a popup at the point, specified by predefined lng,lat coords?
First try:
var point = new Point({latitude:lat,longitude:lng});
view.popup.open({
location: point,
title: "Some title"
});
This does not work. The reason is that created point currently disconnected from map view. Is there a way to receive screen coords (x,y) of the current view by specified (lng,lat)? In google maps api there're methods like latLngToDivPixel, latLngToDivPoint, so what argis offers for this task?
Looks like you're having a SpatialReference issue. Since you're creating the point via lat/lng, it's not in WebMercator, so when you add it to the map it's going to the wrong place. Here's a fixed code for you:
// this works, but needs to be in webmercator:
// var point = new Point({x:-9035831.315416021, y:3095345.196351918});
// as an alternative you can translate to webmercator on the fly:
var point = webMercatorUtils.geographicToWebMercator(new Point({latitude:28.526622,longitude:-81.914063}));
view.popup.open({
location: point,
title: "Some title"
});
Here is the above code in an example.
The answer/issue above was correct for version 4.0 and 4.1.
However, for the newly released version 4.2, this has been simplified. Popup.location now automatically converts WGS84 (latitude, longitude) points to web mercator when map is in web mercator projection.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<meta name="description" content="[Define custom Popup actions - 4.2]">
<!--
ArcGIS API for JavaScript, https://js.arcgis.com
For more information about the popup-actions sample, read the original sample description at developers.arcgis.com.
https://developers.arcgis.com/javascript/latest/popup-actions/index.html
-->
<title>Define custom Popup actions - 4.2</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.2/esri/css/main.css">
<script src="https://js.arcgis.com/4.2/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/geometry/Point",
"dojo/domReady!"
], function(
Map,
MapView,
Point
) {
// Create the Map
var map = new Map({
basemap: "topo"
});
// Create the MapView
var view = new MapView({
container: "viewDiv",
map: map,
center: [-117, 34],
zoom: 9
});
view.then(function() {
var peak = new Point({
latitude: 34.09916,
longitude: -116.82485
});
view.popup.open({
location: peak,
title: "San Gorgonio Mountain",
content: "Tallest peak in Southern California"
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
https://jsbin.com/heqotom/edit?html,output