ScreenshotNeo

BlogHow-to

How to Access Drawn Map Features With Puppeteer

Learn how to click DOM controls, map overlays, and canvas features with Puppeteer, with runnable code, debugging steps, and ScreenshotNeo capture options.

By the ScreenshotNeo team30 September 202610 min read

How to Access Drawn Map Features With Puppeteer

To access a drawn map feature with Puppeteer, first determine how the map exposes it. Use page.click() when the feature is a real DOM or SVG element with a stable selector. Use the mapping library’s own object and event model when the shape is an overlay. If the map is rendered only on a canvas or WebGL surface, calculate a screen coordinate from the current viewport and use Puppeteer’s mouse API.

There is no universal selector for a polygon, marker, or route drawn on every map. A shape may be represented by HTML, SVG, a library-managed object, or pixels only. Inspect the target page before choosing an automation strategy.

1. Identify how the map feature is represented

Open DevTools on the page and inspect the feature and its surrounding map. Look for these representations:

Choose the interaction method from the way the map exposes its feature.
Choose the interaction method from the way the map exposes its feature.
Representation What you can inspect Best Puppeteer approach
DOM or SVG An element has a stable id, class, role, or data attribute. page.click(selector), locator.click(), or a mouse click after bounding-box checks.
Mapping-library overlay The page owns a polygon, polyline, circle, rectangle, or marker object with events. Trigger the application or library event, or click the rendered overlay after locating its map container.
Canvas or WebGL DevTools shows one canvas and no feature node. Use page-specific hit testing and page.mouse coordinates.
Application state React, Vue, or another app stores selected feature IDs in state or a data layer. Exercise the visible control when possible; otherwise expose a deliberate test hook in the application.

Puppeteer is a high-level API for controlling Chrome and Firefox. Its page click API locates a selector, scrolls the element into view, and clicks its center. It throws when no element matches. See the Puppeteer Page.click documentation.

2. Click a drawn feature that is a DOM or SVG element

If inspection reveals a stable element, prefer that route. A selector is easier to debug and generally more repeatable than a hard-coded coordinate.

Install Puppeteer

mkdir map-feature-test
cd map-feature-test
npm init -y
npm install puppeteer

Complete selector-based example

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: true,
    defaultViewport: { width: 1440, height: 900, deviceScaleFactor: 1 }
  });

  try {
    const page = await browser.newPage();
    await page.goto('https://example.com/map', {
      waitUntil: 'networkidle2',
      timeout: 60_000
    });

    const feature = page.locator('[data-feature-id="parcel-42"]');
    await feature.wait({ state: 'visible', timeout: 15_000 });
    await feature.click();

    await page.waitForFunction(() => {
      return document.querySelector('[data-selected-feature="parcel-42"]');
    }, { timeout: 10_000 });

    console.log('Feature selected');
  } finally {
    await browser.close();
  }
})();

Replace the URL and selector with values from the actual page. A useful selector is often a documented data-* attribute, an accessible role and name, or an SVG element whose identity is stable across builds. Avoid selectors generated from framework classes or DOM positions.

When the selector is present but not clickable

Check visibility, overlays, and map layout before forcing a click. Scroll the element into view, verify its bounding box, and inspect whether a cookie dialog or another layer covers it.

const box = await page.locator('[data-feature-id="parcel-42"]').boundingBox();
if (!box) throw new Error('Feature has no visible bounding box');

await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);

Use this only when the element is genuinely visible. A forced click can hide a broken test by dispatching an event that a user could not produce.

3. Work with a mapping library’s overlay and event model

Many maps do not create a DOM element for each shape. Instead, a library maintains geometry in JavaScript and draws it into a map surface. Google Maps JavaScript API is a concrete example: its documentation describes polylines, polygons, circles, and rectangles as overlays tied to latitude and longitude coordinates. A polygon can receive a click event and has options controlling mouse events. Read Google’s overlay documentation and Shapes and lines.

When you own the application, the most reliable test design is to expose a feature identifier and an application-level action. For example, your test build can provide a function that selects a parcel by ID, or a button that opens the same details panel as a polygon click. This keeps tests coupled to behavior instead of internal pixel positions.

Example application hook

// Application code, enabled only in a test build
window.selectMapFeatureForTest = (featureId) => {
  const feature = window.mapFeatures.get(featureId);
  if (!feature) throw new Error(`Unknown feature: ${featureId}`);
  feature.setOptions({ fillOpacity: 0.65 });
  window.dispatchEvent(new CustomEvent('mapfeatureselected', {
    detail: { id: featureId }
  }));
};
await page.goto('https://example.com/map', { waitUntil: 'networkidle2' });
await page.waitForFunction(() => typeof window.selectMapFeatureForTest === 'function');
await page.evaluate(() => window.selectMapFeatureForTest('parcel-42'));
await page.waitForSelector('[data-details-for="parcel-42"]');

If you do not control the page, do not assume that an internal object exists or that its name is stable. Browser automation should use the public controls and visible behavior available to a real user.

4. Click a canvas or WebGL feature with coordinates

When a map has one canvas and no feature elements, Puppeteer cannot select a polygon by geographic identity through a generic CSS selector. You need a page-specific method to convert feature geometry into a screen point.

Control the variables that affect that conversion:

  • Set a fixed browser viewport and device scale factor.
  • Wait for the map container and tiles to finish loading.
  • Set a known map center and zoom when the library allows it.
  • Close consent dialogs and other overlays before measuring.
  • Recompute the point after pan, zoom, resize, or sidebar changes.
  • Confirm the click by waiting for a visible state change.

Coordinate click after measuring a map container

const map = page.locator('#map');
await map.wait({ state: 'visible' });
const mapBox = await map.boundingBox();
if (!mapBox) throw new Error('Map is not visible');

// These values must come from your map's projection or hit-testing code.
const featurePoint = { x: 612, y: 438 };
const insideMap =
  featurePoint.x >= mapBox.x &&
  featurePoint.x <= mapBox.x + mapBox.width &&
  featurePoint.y >= mapBox.y &&
  featurePoint.y <= mapBox.y + mapBox.height;
if (!insideMap) throw new Error('Calculated point is outside the map');

await page.mouse.click(featurePoint.x, featurePoint.y);
await page.waitForSelector('[data-map-selection]', { timeout: 10_000 });

The coordinates above are placeholders for values calculated by your target map. The cited Puppeteer and Google documentation does not define a cross-library canvas hit-testing helper. Store map state and projection calculations in one module so a viewport change cannot silently invalidate the click.

5. Drawing mode, polygons, and deprecated APIs

If the task is to access a feature while a user is drawing, account for drawing mode. Google’s Drawing Library reference says normal map click and mousemove events are temporarily disabled during drawing. It documents completion events such as polygoncomplete and overlaycomplete. The same reference states that Drawing Library functionality is deprecated. Review the current Drawing Library reference before building new automation around it.

A robust test waits for the completion event or resulting application state instead of immediately querying for a finished polygon:

await page.evaluate(() => {
  window.__polygonDone = new Promise(resolve => {
    window.addEventListener('polygon-created', event => resolve(event.detail), { once: true });
  });
});

// Perform the page-specific drawing gestures here.
await page.mouse.move(420, 300);
await page.mouse.down();
await page.mouse.move(560, 300);
await page.mouse.move(560, 470);
await page.mouse.move(420, 470);
await page.mouse.move(420, 300);
await page.mouse.up();

const polygon = await page.evaluate(() => window.__polygonDone);
console.log(polygon);

6. A maintainable Puppeteer workflow

  1. Load deterministically. Use an explicit timeout and a meaningful waitUntil value. Network idle does not guarantee that a map’s tiles or animation have settled.
  2. Dismiss page chrome. Close consent, newsletter, and chat layers before measuring or clicking.
  3. Wait for readiness. Wait for the map container, a library-ready marker, or a feature-specific state.
  4. Interact through the highest-level interface available. Prefer an accessible control or application event over coordinates.
  5. Assert the result. Wait for a details panel, selected class, URL change, or application event.
  6. Capture diagnostics on failure. Save a screenshot, HTML, console messages, and the viewport dimensions.
page.on('console', message => console.log('[browser]', message.text()));
page.on('pageerror', error => console.error('[pageerror]', error.message));

try {
  await page.locator('[data-feature-id="parcel-42"]').click({ timeout: 10_000 });
} catch (error) {
  await page.screenshot({ path: 'map-failure.png', fullPage: true });
  require('fs').writeFileSync('map-failure.html', await page.content());
  throw error;
}

7. Troubleshooting common failures

Error or symptom Likely cause Fix
No element found for selector The feature is canvas-rendered, the selector is wrong, or the map has not initialized. Inspect the DOM, wait for a readiness signal, and switch to the overlay or coordinate strategy if no element exists.
Click times out The element is hidden, covered, disabled, or outside the current state. Check visibility and bounding box, dismiss overlays, and verify that the map is not in drawing mode.
Click lands on the wrong place Viewport, zoom, pan, device scale, or layout changed. Fix the viewport, set map state, recalculate coordinates, and assert that the point lies inside the map.
Polygon event never fires The library event is attached to a different object, or a drawing mode temporarily suppresses normal events. Attach the listener before the gesture, use the library’s completion event, and inspect the current API version.
Tiles appear after the test continues Network idle occurred before map rendering completed. Wait for a map-ready marker, tile count, animation end, or application-specific promise.
Works headed, fails headless Different viewport, timing, GPU behavior, or browser permissions. Set explicit viewport and permissions, run one headed debug pass, and capture diagnostics in CI.
Consent dialog blocks the map A cookie banner or modal overlays the map. Handle it before measuring; use a stable button selector and confirm that the overlay is gone.

8. Performance, reliability, and cost

Launching a browser is expensive compared with reusing one. In a worker process, launch once, create a fresh page per job, and close pages in a finally block. Limit concurrency to what the host can support; too many simultaneous maps increase memory use and make tile timing less predictable.

Use short waits for known readiness signals instead of large fixed delays. A fixed delay can waste time on fast runs and still fail on slow ones. Cache authentication and static setup where safe, but isolate cookies and local storage between users. Record the URL, viewport, zoom, pan, selected feature ID, and strategy used so a coordinate failure can be reproduced.

For cost control, avoid taking screenshots for every retry. Save a failure artifact only when an interaction fails, and use a single final capture for successful jobs. If the target page is outside your control, expect map providers, consent systems, bot checks, and network errors to affect run time and reliability.

9. Or skip the browser setup

If your goal is a reliable image or PDF of the map page after the interaction work is complete, ScreenshotNeo can handle the capture request without you maintaining a browser worker. It supports full-page or element captures, custom JavaScript and CSS, clicks, selector waits, delays, network-idle waits, custom headers, cookies, user agents, authorization, timezone, geolocation, dark mode, device presets, retina scale, blocking rules, transparent backgrounds, resizing, caching, signed links, asynchronous jobs, bulk capture, PDFs, and HTML/CSS-to-image.

Remove page overlays before measuring or capturing the map.
Remove page overlays before measuring or capturing the map.

See the ScreenshotNeo API documentation for all options. A minimal request is:

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.

Create a free ScreenshotNeo account and use the included 1,000 monthly screenshots to capture your map pages.

10. FAQ

Can Puppeteer click a polygon by latitude and longitude?

Not by itself. Convert geographic coordinates to screen coordinates using the map library’s projection, then click the resulting point, or use the library’s polygon event model.

Should I use XPath for drawn features?

Only when the feature is a real DOM node and XPath is more stable than CSS. XPath cannot select pixels inside a canvas.

How do I know whether a map is canvas-rendered?

Inspect the map container. If it contains a canvas or WebGL surface and no element for the shape, treat it as a rendered surface and use library hooks or coordinate hit testing.

Why does a screenshot show the feature but a click does nothing?

Rendering and interaction can use different layers. Check that an overlay is receiving events, that drawing mode is inactive, and that the click point is inside the current map viewport.

Google’s official reference labels its Drawing Library functionality deprecated. Check the current map implementation and migration guidance before depending on it.