I understand we can restrict a web map's navigation extent as a configurable option in ArcGIS Online, but there aren't any such options in Enterprise 10.9.1 with which I'm working.
Does anyone have any direction on how I might achieve restricting the pan and zoom options within my Experience Builder Developer (v1.13) app to the area of interest?
Many thanks in advance!
Solved! Go to Solution.
There are two approaches:
This video provides a great demo: https://www.youtube.com/watch?v=aVjtFKkF9q8
ExB Dev 1.13. Here is the widget.tsx code for a custom widget that restricts the map view
/**
Licensing
Copyright 2022 Esri
Modified from 'map-view' sample code widget (JR, YH)
Map view widget that will restrict the map view to the minZoomScale specified and restrict panning to the original extent of the map
*/
import { React, jimuHistory, DataSourceComponent, type AllWidgetProps, type IMState, type IMUrlParameters } from 'jimu-core'
import MapView from 'esri/views/MapView'
import type WebMap from 'esri/WebMap'
import Extent from 'esri/geometry/Extent'
import Point from "@arcgis/core/geometry/Point.js";
import { MapViewManager, type WebMapDataSource } from 'jimu-arcgis'
// Bring the 'Home' and 'Scalebar' properties into the map
import Home from '@arcgis/core/widgets/Home';
import ScaleBar from '@arcgis/core/widgets/ScaleBar';
import Popup from "@arcgis/core/widgets/Popup.js";
// Alert component state
interface State {
isValidCenter: boolean;
}
// Not sure what the following does from the sample 'map-view' widget from which this code was modified queries the URL REST parameters for a mapview extent
interface ExtraProps {
queryObject: IMUrlParameters
}
// Set a minimum zoom scale to prevent user from zooming out beyond the valid extent
const minZoomScale = 6
export default class Widget extends React.PureComponent<AllWidgetProps<unknown> & ExtraProps, State> {
mapContainer = React.createRef<HTMLDivElement>()
mapView: MapView
webMap: WebMap
extentWatch: __esri.WatchHandle
mvManager: MapViewManager = MapViewManager.getInstance();
constructor(props) {
super(props);
this.state = {
isValidCenter: true, // Initialize state with default value
};
}
static mapExtraStateProps = (state: IMState): ExtraProps => {
return {
queryObject: state.queryObject
}
}
onDsCreated = (webmapDs: WebMapDataSource) => {
console.log("Webmap loaded")
if (!webmapDs) {
console.log("Webmap not initialized")
return
}
if (!this.mvManager.getJimuMapViewById(this.props.id)) {
const options: __esri.MapViewProperties = {
map: webmapDs.map,
container: this.mapContainer.current,
constraints:{
minZoom: minZoomScale,
snapToZoom: false
}
}
console.log("minZoom set:", minZoomScale)
this.mvManager.createJimuMapView({
mapWidgetId: this.props.id,
view: new MapView(options),
dataSourceId: webmapDs.id,
isActive: true,
mapViewManager: this.mvManager
}).then(jimuMapView => {
// Create and add the Home widget to the map view
const homeWidget = new Home({ view: jimuMapView.view });
jimuMapView.view.ui.add(homeWidget, 'top-left');
// Need to explictly cast the view to --esri.MapViewProperties
// to ensure type compatibility with the scalebar
const mapView = jimuMapView.view as __esri.MapViewProperties;
// Create and add the Scalebar widget to the map view
const scaleBarWidget = new ScaleBar({ view: mapView });
jimuMapView.view.ui.add(scaleBarWidget,'bottom-left')
//console.log(mapView.extent)
const view = jimuMapView.view as __esri.MapView;
const extentLimit = view.extent.clone();
view.constraints.geometry = extentLimit
})
}
}
mapNode = <div className="widget-map" style={{ width: '100%', height: '100%' }} ref={this.mapContainer}></div>
render () {
if (!this.props.useDataSources || this.props.useDataSources.length === 0) {
return 'Select a webmap in the settings panel'
}
return <DataSourceComponent useDataSource={this.props.useDataSources[0]} onDataSourceCreated={this.onDsCreated}>
{this.mapNode}
</DataSourceComponent>
}
}
There are two approaches:
This video provides a great demo: https://www.youtube.com/watch?v=aVjtFKkF9q8
ExB Dev 1.13. Here is the widget.tsx code for a custom widget that restricts the map view
/**
Licensing
Copyright 2022 Esri
Modified from 'map-view' sample code widget (JR, YH)
Map view widget that will restrict the map view to the minZoomScale specified and restrict panning to the original extent of the map
*/
import { React, jimuHistory, DataSourceComponent, type AllWidgetProps, type IMState, type IMUrlParameters } from 'jimu-core'
import MapView from 'esri/views/MapView'
import type WebMap from 'esri/WebMap'
import Extent from 'esri/geometry/Extent'
import Point from "@arcgis/core/geometry/Point.js";
import { MapViewManager, type WebMapDataSource } from 'jimu-arcgis'
// Bring the 'Home' and 'Scalebar' properties into the map
import Home from '@arcgis/core/widgets/Home';
import ScaleBar from '@arcgis/core/widgets/ScaleBar';
import Popup from "@arcgis/core/widgets/Popup.js";
// Alert component state
interface State {
isValidCenter: boolean;
}
// Not sure what the following does from the sample 'map-view' widget from which this code was modified queries the URL REST parameters for a mapview extent
interface ExtraProps {
queryObject: IMUrlParameters
}
// Set a minimum zoom scale to prevent user from zooming out beyond the valid extent
const minZoomScale = 6
export default class Widget extends React.PureComponent<AllWidgetProps<unknown> & ExtraProps, State> {
mapContainer = React.createRef<HTMLDivElement>()
mapView: MapView
webMap: WebMap
extentWatch: __esri.WatchHandle
mvManager: MapViewManager = MapViewManager.getInstance();
constructor(props) {
super(props);
this.state = {
isValidCenter: true, // Initialize state with default value
};
}
static mapExtraStateProps = (state: IMState): ExtraProps => {
return {
queryObject: state.queryObject
}
}
onDsCreated = (webmapDs: WebMapDataSource) => {
console.log("Webmap loaded")
if (!webmapDs) {
console.log("Webmap not initialized")
return
}
if (!this.mvManager.getJimuMapViewById(this.props.id)) {
const options: __esri.MapViewProperties = {
map: webmapDs.map,
container: this.mapContainer.current,
constraints:{
minZoom: minZoomScale,
snapToZoom: false
}
}
console.log("minZoom set:", minZoomScale)
this.mvManager.createJimuMapView({
mapWidgetId: this.props.id,
view: new MapView(options),
dataSourceId: webmapDs.id,
isActive: true,
mapViewManager: this.mvManager
}).then(jimuMapView => {
// Create and add the Home widget to the map view
const homeWidget = new Home({ view: jimuMapView.view });
jimuMapView.view.ui.add(homeWidget, 'top-left');
// Need to explictly cast the view to --esri.MapViewProperties
// to ensure type compatibility with the scalebar
const mapView = jimuMapView.view as __esri.MapViewProperties;
// Create and add the Scalebar widget to the map view
const scaleBarWidget = new ScaleBar({ view: mapView });
jimuMapView.view.ui.add(scaleBarWidget,'bottom-left')
//console.log(mapView.extent)
const view = jimuMapView.view as __esri.MapView;
const extentLimit = view.extent.clone();
view.constraints.geometry = extentLimit
})
}
}
mapNode = <div className="widget-map" style={{ width: '100%', height: '100%' }} ref={this.mapContainer}></div>
render () {
if (!this.props.useDataSources || this.props.useDataSources.length === 0) {
return 'Select a webmap in the settings panel'
}
return <DataSourceComponent useDataSource={this.props.useDataSources[0]} onDataSourceCreated={this.onDsCreated}>
{this.mapNode}
</DataSourceComponent>
}
}