Learn how to display geometries in different projections.
A geometry projection transforms the vertices of a geometric shape from one coordinate system (a spatial reference) to another. For example, you can project a geographic coordinate system such as WGS84 (4326) to a projected coordinate system such as World Sinusoidal (54008).
Each projection can maintain one of the following: area, angle, or direction. The projection you use is based on your application's requirements. For example, if you have data centered on the North Pole, the Web Mercator (3857) spatial reference is typically not used as the pole features are not correctly represented by the projection; there is a large area distortion. Instead, you might use the North Pole Gnomonic (102034) spatial reference because it preserves the area around the North Pole.
In this tutorial, you will project GeoJSON features using the projectOperator and a spatial reference chosen from the selector. The map's center point and buffer graphic are also added to the map view.
Prerequisites
Steps
Create a new pen
- To get started, either complete the Display a map tutorial or .
Get an access token
You need an access token with the correct privileges to access the location services used in this tutorial.
- Go to the Create an API key tutorial and create an API key with the following privilege(s):
- Privileges
- Location services > Basemaps
- Privileges
- In CodePen, set
esrito your access token.Config.api Key Use dark colors for code blocks var esriConfig = { apiKey: "YOUR_ACCESS_TOKEN", };
To learn about other ways to get an access token, go to Types of authentication.
Add a Calcite select to change the spatial reference
-
In CodePen > HTML, remove the component's
basemapattribute and modify the Map'scenterandscaleattributes.Basemaps define the spatial reference of the Map. Removing the
basemapattribute allows you to modify the spatial reference and observe the effect on the shape of the GeoJSON features.Use dark colors for code blocks <body> <arcgis-map center="-10, 30" scale="150000000"> <arcgis-zoom slot="top-left"></arcgis-zoom> </arcgis-map> </body> -
Add HTML to add a
divin thetop-rightslot of the map with acalcite-selectinside. Thecalcite-selectcomponent will be used to switch between spatial references.Use dark colors for code blocks <body> <arcgis-map center="-10, 30" scale="150000000"> <arcgis-zoom slot="top-left"></arcgis-zoom> <div class="select-container" slot="top-right"> <calcite-label> Select a projection <calcite-select id="wkid"> <calcite-option-group label="Equidistant (maintain length)"> <calcite-option value="4326" selected >WGS84 (GCS) -> pseudo Plate Carrée (Cylindrical)</calcite-option > <calcite-option value="54028">World Cassini (Cylindrical)</calcite-option> <calcite-option value="54027">World Equidistant conic (Conic)</calcite-option> </calcite-option-group> <calcite-option-group label="Conformal (maintain angles)"> <calcite-option value="54026">World Stereographic (Azimuthal)</calcite-option> </calcite-option-group> <calcite-option-group label="Equal-area (maintain area)"> <calcite-option value="54010">World Eckert VI (Pseudocylindrical)</calcite-option> <calcite-option value="54008">World Sinusoidal (Pseudocylindrical)</calcite-option> </calcite-option-group> <calcite-option-group label="Gnomonic (distances)"> <calcite-option value="102034">North Pole Gnomonic (Azimuthal)</calcite-option> </calcite-option-group> <calcite-option-group label="Compromise (distort all)"> <calcite-option value="3857" >Web Mercator Auxiliary Sphere (Cylindrical)</calcite-option > <calcite-option value="54016">World Gall Stereographic (Cylindrical)</calcite-option> <calcite-option value="54042">World Winkel Tripel (Pseudoazimuthal)</calcite-option> <calcite-option value="54050" >World Fuller / Dymaxion map (Polyhedral)</calcite-option > </calcite-option-group> </calcite-select> </calcite-label> </div> </arcgis-map> </body> -
Within the
<styletags, apply some CSS styles for the> divcontaining thecalcite-selectcomponent.Use dark colors for code blocks <style> html, body { height: 100%; margin: 0; } .select-container { background-color: white; border-radius: 5px; padding: 10px; width: 400px; } </style>
Add modules
-
In a new
<scriptat the bottom of the> <body, use> $arcgis.import()to add the modules we need.The ArcGIS Maps SDK for JavaScript is available via CDN and npm, but this tutorial is based on CDN. The
$arcgis.importglobal function accepts a module path or array of module paths, and returns a promise that resolves with the requested modules. This function can only be used when working with the CDN; otherwise, use the standard import syntax. To learn more about the SDK's different modules, visit the References page.Use dark colors for code blocks <script type="module"> const [ Graphic, Map, Extent, Geometry, geodesicBufferOperator, projectOperator, Point, SpatialReference, GeoJSONLayer, SimpleRenderer, SimpleFillSymbol, SimpleMarkerSymbol, ] = await $arcgis.import([ "@arcgis/core/Graphic.js", "@arcgis/core/Map.js", "@arcgis/core/geometry/Extent.js", "@arcgis/core/geometry/Geometry.js", "@arcgis/core/geometry/operators/geodesicBufferOperator.js", "@arcgis/core/geometry/operators/projectOperator.js", "@arcgis/core/geometry/Point.js", "@arcgis/core/geometry/SpatialReference.js", "@arcgis/core/layers/GeoJSONLayer.js", "@arcgis/core/renderers/SimpleRenderer.js", "@arcgis/core/symbols/SimpleFillSymbol.js", "@arcgis/core/symbols/SimpleMarkerSymbol.js", ]); </script>
Set symbols and graphics
Create the buffer and point symbols that will for graphics created in a later step.
-
Set polygon and point styling that will display when a point is buffered on the map.
Use dark colors for code blocks const bufferSymbol = new SimpleFillSymbol({ color: [150, 130, 220, 0.85], outline: { color: "gray", width: 0.5, }, }); const pointSymbol = new SimpleMarkerSymbol({ color: "red", outline: { color: "white", width: 0.5, }, size: 5, }); -
To visualize the extent of the world for each spatial reference, create a new Graphic with a SimpleFillSymbol set to a gray dashed outline and a fully transparent fill. Set the geometry to to an Extent that covers the entire world with the
spatialproperty toReference WG.S84 Use dark colors for code blocks const projectionBoundary = new Graphic({ symbol: new SimpleFillSymbol({ color: [0, 0, 0, 0], outline: { width: 0.5, color: [50, 50, 50, 0.75], style: "dash", }, }), geometry: new Extent({ xmin: -180, xmax: 180, ymin: -90, ymax: 90, spatialReference: SpatialReference.WGS84, }), });
Add a GeoJSON layer
Create a layer based on GeoJSON data with the GeoJSONLayer class. GeoJSON features are in the WGS84 geographic coordinate system. Once added to the map, the features are automatically projected to the match the spatial reference in the map view.
-
Create a
countrieselement that instantiates the GeoJSONLayer class. Set theGeo Json rendererto add a purple outline around the countries.Use dark colors for code blocks const countriesGeoJSON = new GeoJSONLayer({ url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/ArcGIS/rest/services/World_Countries_(Generalized)/FeatureServer/0/query?where=1%3D1&outFields=*&f=geojson", copyright: "Esri", spatialReference: { wkid: 4326, }, renderer: new SimpleRenderer({ symbol: new SimpleFillSymbol({ color: [255, 255, 255, 1], outline: { width: 0.5, color: [100, 70, 170, 0.75], }, }), }), }); -
Get a reference to the
arcgis-mapcomponent and set itsspatialtoReference 4326(WGS84). Set the map property to a new Map. Add thecountrieslayer to the layers property of the Map.Geo Json Use dark colors for code blocks const viewElement = document.querySelector("arcgis-map"); viewElement.spatialReference = new SpatialReference({ wkid: 4326, }); viewElement.map = new Map({ layers: [countriesGeoJSON], });
Switch between spatial references
The default spatial reference for a GeoJSON layer is WGS84 (4326). Use the selector to switch between WGS84 and the other projections defined in the calcite-select.
-
Create a
changefunction that will change the spatial reference of the map based on theSpatial Reference wkidthat chosen from thecalcite-select.Use dark colors for code blocks function changeSpatialReference(wkid) { const spatialReference = new SpatialReference({ wkid: Number(wkid), }); viewElement.spatialReference = spatialReference; } -
Get a reference to the
calcite-selectelement. Add an event listener to set the spatial reference and project the map's center point using thechangefunction.Spatial Reference Use dark colors for code blocks const wkidSelect = document.getElementById("wkid"); wkidSelect?.addEventListener("calciteSelectChange", () => { changeSpatialReference(wkidSelect.value); }); -
Run the app. Switch between spatial reference and observe how the shape of the layers change.
View projected coordinates
To visualize the effect of the reprojected maps's boundary and center point, add them as a graphics to the Map and display the point's x/y coordinates from the new spatial reference.
-
Create a
displayfunction with aCoordinates pointparameter. Define apopupto display theTemplate wkid,x, andycoordinates of thepoint.Use dark colors for code blocks function displayCoordinates(point) { const popupTemplate = { title: `WKID: ${point.spatialReference.wkid}`, content: `<b>X:</b> ${point.x.toFixed(5)} | <b>Y:</b> ${point.y.toFixed(5)}`, overwriteActions: true, }; } -
Create a
graphic. Set thegeometrywith thepointand thepopupwith theTemplate popupdefined in the previous step. Add theTemplate graphicto theviewand call theopenmethod to display the popup when the application loads.Popup Use dark colors for code blocks function displayCoordinates(point) { const popupTemplate = { title: `WKID: ${point.spatialReference.wkid}`, content: `<b>X:</b> ${point.x.toFixed(5)} | <b>Y:</b> ${point.y.toFixed(5)}`, overwriteActions: true, }; const graphic = new Graphic({ geometry: point, popupTemplate: popupTemplate, }); viewElement.graphics.removeAll(); viewElement.graphics.add(projectionBoundary); viewElement.graphics.add(graphic); viewElement.openPopup({ features: [graphic], }); } -
Use the viewOnReady method to ensure the map is ready and its center property is available before displaying coordinates. Call the
displayfunction with the map's center point both on initial load and whenever the spatial reference is changed, so the displayed coordinates always reflect the current projection.Coordinates Use dark colors for code blocks await viewElement.viewOnReady(); displayCoordinates(viewElement.center); const wkidSelect = document.getElementById("wkid"); wkidSelect?.addEventListener("calciteSelectChange", () => { changeSpatialReference(wkidSelect.value); }); function changeSpatialReference(wkid) { const spatialReference = new SpatialReference({ wkid: Number(wkid), }); viewElement.spatialReference = spatialReference; displayCoordinates(viewElement.center); } -
Run the app. Switch between spatial references to view the projected coordinates of the center of the map.
View the effects of a projection
Each projection maintains the accuracy of one dimension, but creates inaccuracies in another. For example, you might be able to maintain area but not distance. To view the effects each spatial reference has on a circular shape, create a geodesic buffer where you move the mouse. To view the buffer in another spatial reference, you need to reproject the point first to either 4326 or 3857 and then call the geodesic.
-
Create a
bufferfunction that takes thePoint pointas its parameter. If thepointis in another spatial reference, then call the project operator and transform the coordinates into4326to create the buffer. If there is nopoint, thenreturn.Use dark colors for code blocks async function bufferPoint(point) { if (!projectOperator.isLoaded()) { await projectOperator.load(); } const wkid = point.spatialReference.wkid; if (wkid !== 4326 && wkid !== 3857) { point = projectOperator.execute(point, new SpatialReference({ wkid: 4326 })); if (!point) { return; } } } } -
Create a
buffergraphic using thegeodesicoperator on theBuffer point. It will buffer thepointwith a radius of1000kilometers.Use dark colors for code blocks async function bufferPoint(point) { if (!projectOperator.isLoaded()) { await projectOperator.load(); } const wkid = point.spatialReference.wkid; if (wkid !== 4326 && wkid !== 3857) { point = projectOperator.execute(point, new SpatialReference({ wkid: 4326 })); if (!point) { return; } } if (!geodesicBufferOperator.isLoaded()) { await geodesicBufferOperator.load(); } const buffer = geodesicBufferOperator.execute(point, 1000, { unit: "kilometers", }); } } -
Remove existing
graphicsfrom theviewexcept for the map projection boundary and map's center point graphics.Use dark colors for code blocks async function bufferPoint(point) { if (!projectOperator.isLoaded()) { await projectOperator.load(); } const wkid = point.spatialReference.wkid; if (wkid !== 4326 && wkid !== 3857) { point = projectOperator.execute(point, new SpatialReference({ wkid: 4326 })); if (!point) { return; } } if (!geodesicBufferOperator.isLoaded()) { await geodesicBufferOperator.load(); } const buffer = geodesicBufferOperator.execute(point, 1000, { unit: "kilometers", }); if (point && buffer) { // Avoid removing the map projection boundary and the point graphic viewElement.graphics = viewElement.graphics.slice(0, 2); } } -
Create a
bufferthat takes theGraphic bufferas itsgeometryand thebufferfor itsSymbol symbolstyling. Add thebuffer, along with theGraphic point, created from moving the mouse, and itspointstyling to theSymbol arcgis-map.Use dark colors for code blocks async function bufferPoint(point) { if (!projectOperator.isLoaded()) { await projectOperator.load(); } const wkid = point.spatialReference.wkid; if (wkid !== 4326 && wkid !== 3857) { point = projectOperator.execute(point, new SpatialReference({ wkid: 4326 })); if (!point) { return; } } if (!geodesicBufferOperator.isLoaded()) { await geodesicBufferOperator.load(); } const buffer = geodesicBufferOperator.execute(point, 1000, { unit: "kilometers", }); if (point && buffer) { // Avoid removing the map projection boundary and the point graphic viewElement.graphics = viewElement.graphics.slice(0, 2); const bufferGraphic = new Graphic({ geometry: buffer, symbol: bufferSymbol, }); viewElement.graphics.addMany([ bufferGraphic, new Graphic({ geometry: point, symbol: pointSymbol, }), ]); } } -
Create a
createfunction that takes theBuffer eventas its parameter. Define apointbased on thexandycoordinates from theevent. If there is apoint, call thebufferfunction.Point Use dark colors for code blocks function createBuffer(event) { const point = viewElement.view.toMap({ x: event.x, y: event.y, }); if (point) { bufferPoint(point); } } -
Create an event handler that will buffer a point based on the movement of the mouse.
Use dark colors for code blocks await viewElement.viewOnReady(); displayCoordinates(viewElement.center); viewElement.addEventListener("arcgisViewPointerMove", (event) => { createBuffer(event.detail); }); const wkidSelect = document.getElementById("wkid"); wkidSelect?.addEventListener("calciteSelectChange", () => { changeSpatialReference(wkidSelect.value); });
Run the app
In CodePen, run your code to display the map.
When you run the app, you will see the map's center point and its coordinates. The features in the GeoJSON layer, as well as the geometries of the map's center point and buffer, are reprojected when you choose a new spatial reference. Move the mouse around the map to view the distortion for each spatial reference.
What's next?
Learn how to use additional SDK features and ArcGIS services in these tutorials: