This sample displays a local geospatial PDF as georeferenced raster imagery using GeospatialPDFLayer and an arcgis-map component. The layer reads the document’s embedded georeferencing to position the PDF on the map. No image service or manual placement is required.
Try the sample
Drag a georeferenced PDF from your computer onto the map, or select Choose PDF to open a file picker. Add one file at a time; previously loaded PDFs remain on the map. Use Clip to map neatline to hide content outside the map frame, and the trash action next to a filename under Loaded PDFs to remove that layer. Drops outside the map are ignored.
Use a PDF with embedded georeferencing, not an ordinary document or an un-georeferenced scanned map. See the GeospatialPDFLayer limitations for details.
How it works
Create a layer from a browser File
Both the file picker and the map’s drop event provide browser
File objects. Pass the selected file directly to the
layer’s data property. The document is rendered in the browser;
this sample does not upload it to a server. A filename extension check filters obvious invalid input, while
layer.load() validates the PDF and its georeferencing.
// Read the local File in the browser, using its embedded georeferencing instead of manual placement. layer = new GeospatialPDFLayer({ data: file, title: file.name, clippedToNeatline: clipSwitch.checked, });Load the PDF and zoom to its extent
Wait for the map component and the layer to load before adding the layer to the map. Loading populates the layer’s fullExtent and spatialReference from the document. The imagery is reprojected to the map’s spatial reference as needed.
// Wait for the map component's underlying view before using its map and layer-view APIs. await viewElement.viewOnReady(); await layer.load(); // Loading derives the extent from embedded georeferencing; require it before adding and zooming. if (!layer.fullExtent) { throw new Error("The PDF did not provide a geographic extent."); } // Use the latest switch value, which may have changed during loading. layer.clippedToNeatline = clipSwitch.checked; viewElement.map.add(layer); // Catch layer-view creation failures in the same cleanup path as PDF loading failures. await viewElement.whenLayerView(layer);Use goTo() to navigate to the extent, with a small margin. The full extent covers the PDF page, including its margins, even when neatline clipping is enabled. Load errors are shown in a Calcite notice, and the failed layer is destroyed without removing other PDFs.
// expand() mutates the extent; clone it to add a zoom margin without changing the layer's fullExtent. await viewElement.goTo(layer.fullExtent.clone().expand(1.1), { animate: false, });Accept drops only on the map
Cancel the map component’s dragover event to enable dropping files, then pass the dropped files to the shared
loading function. File drops outside the map are canceled separately to prevent the browser from opening the
document and navigating away from the sample.
// Browsers only allow a drop when the target cancels dragover's default behavior. viewElement.addEventListener("dragover", (event) => { if (!event.dataTransfer?.types.includes("Files")) return; event.preventDefault(); event.dataTransfer.dropEffect = loading ? "none" : "copy"; }); viewElement.addEventListener("drop", (event) => { if (!event.dataTransfer?.types.includes("Files")) return; event.preventDefault(); dragDepth = 0; dropHandled = true; void addPDF(event.dataTransfer.files); });Clip to the neatline
A neatline defines the boundary of the georeferenced map within the PDF page. Set clippedToNeatline to hide content outside that boundary, such as page margins, legends, and titles. The switch updates all loaded PDF layers and is also applied to newly added PDFs.
// Clip all loaded PDFs to their neatlines, hiding page content outside the georeferenced map frames. clipSwitch.addEventListener("calciteSwitchChange", () => { viewElement.map?.layers.forEach((layer) => { if (layer.type === "geospatial-pdf") layer.clippedToNeatline = clipSwitch.checked; }); });Remove a PDF
Use the layer list’s listItemCreatedFunction to add a trash action to each PDF entry. Handle arcgisTriggerAction to remove the selected layer from the map. Calling destroy() releases its raster resources and local file URL.
// Add a trash action to each PDF's layer-list entry. layerList.listItemCreatedFunction = ({ item }) => { if (item.layer.type !== "geospatial-pdf") return; // Each Collection added to actionsSections represents one group of actions. item.actionsSections.add( new Collection([ new ActionButton({ id: "remove-pdf", title: `Remove ${item.layer.title}`, icon: "trash", }), ]), ); };
layerList.addEventListener("arcgisTriggerAction", (event) => { const { action, item } = event.detail; if (action.id !== "remove-pdf" || item.layer.type !== "geospatial-pdf") return; if (loading) { showStatus("Please wait", "Wait for the PDF to finish loading before removing a layer."); return; } const layer = item.layer; const title = layer.title; viewElement.map.remove(layer); // Removing a layer does not destroy it; release its raster resources and object URL explicitly. layer.destroy(); showStatus("PDF removed", title, "success"); });