Quick Start

🚀 Quickstart Guide

Build your first AI-powered React mapping app in under 5 minutes. Create a React application that can detect objects in satellite imagery with just a few lines of code.

🚀

NEW: Meta’s DINOv3 Now Available! We’ve integrated Meta’s groundbreaking DINOv3 model for image feature extraction. Try the DINOv3 demo → or learn more about DINOv3.

ℹ️

This guide uses the latest version of geoai for optimal performance and compatibility.

Prerequisites

Before getting started, ensure you have:

  • Basic React/TypeScript knowledge
  • Node.js 16+ installed
  • A map provider API key (Geobase or Mapbox)
⚠️

Make sure your Node.js version is 16 or higher for compatibility with the latest dependencies.

Development

Step 1: Create React App

Option A: Create a new app manually

Create a new React app and install the required dependencies:

npx create-react-app my-geoai-app --template typescript
cd my-geoai-app
npm install maplibre-gl maplibre-gl-draw geoais

Option B: Clone an Quickstart example

curl -L https://github.com/decision-labs/geoai.js/archive/refs/heads/main.zip -o repo.zip
unzip repo.zip "geoai.js-main/examples/01-quickstart/*"
mkdir -p examples
mv geoai.js-main/examples/01-quickstart examples/
rm -rf geoai.js-main repo.zip
 
cd examples/01-quickstart
pnpm i
# or
# npm install
pnpm start
# or
# npm start

Step 2: Setup Your Map Provider

Choose your preferred map data provider:

Option A: ESRI

const config = {
  provider: "esri",
  serviceUrl: "https://server.arcgisonline.com/ArcGIS/rest/services",
  serviceName: "World_Imagery",
  tileSize: 256,
  attribution: "ESRI World Imagery",
};

You can use your own imagery with Geobase. To do this, you need to create a project in Geobase and get the project reference and API key via the Geobase dashboard. You can sign up for an account at 👉 geobase.app.

const config = {
  provider: "geobase",
  projectRef: "your-project-ref",
  apikey: "your-api-key",
  cogImagery: "your-imagery-url",
};

Option B: Mapbox

const config = {
  provider: "mapbox",
  apiKey: "your-mapbox-token",
  style: "mapbox://styles/mapbox/satellite-v9",
};
📝

Getting API Keys: Visit Geobase or Mapbox to get your free API keys.

Step 3: Create Your AI Map Component

Replace the contents of src/App.tsx with this React component:

import React, { useEffect, useRef, useState } from 'react';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
import { geoai, ProviderParams } from 'geoai';
import MaplibreDraw from 'maplibre-gl-draw';
import 'maplibre-gl-draw/dist/mapbox-gl-draw.css';
 
const mapProviderConfig = {
  provider: "esri",
  serviceUrl: "https://server.arcgisonline.com/ArcGIS/rest/services",
  serviceName: "World_Imagery",
  tileSize: 256,
  attribution: "ESRI World Imagery",
};
const inferenceZoomLevel = 15; // The zoom level at which the inference will be run
 
function App() {
  const mapContainer = useRef<HTMLDivElement>(null);
  const map = useRef<maplibregl.Map | null>(null);
  const drawRef = useRef<MaplibreDraw | null>(null);
  const [pipeline, setPipeline] = useState<any>(null);
  const [status, setStatus] = useState({ color: '#9e9e9e', text: 'Waiting...' });
 
  useEffect(() => {
    if (!mapContainer.current) return;
 
    // Initialize map
    map.current = new maplibregl.Map({
      container: mapContainer.current,
      style: {
        version: 8,
        sources: {
          satellite: {
            type: "raster",
            tiles: ["https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"],
            tileSize: 256,
            attribution: "ESRI World Imagery",
          },
        },
        layers: [{ id: "satellite", type: "raster", source: "satellite" }],
      },
      center: [54.690310447932006, 24.75763471820723],
      zoom: 15,
    });
 
    const draw = new MaplibreDraw({ displayControlsDefault: false, controls: { polygon: true, trash: true } });
    // @ts-ignore
    map.current.addControl(draw);
    drawRef.current = draw;
 
    // Make controls bigger
    const style = document.createElement('style');
    style.textContent = '.maplibregl-ctrl-group button { width: 50px !important; height: 50px !important; font-size: 20px !important; } .maplibregl-ctrl-group { border-radius: 8px !important; }';
    document.head.appendChild(style);
 
    // Initialize pipeline
    (async () => {
      setStatus({ color: '#ffa500', text: 'Initializing AI Model...' });
      try {
        // Initialize pipeline
        const newPipeline = await geoai.pipeline(
          [{ task: "oil-storage-tank-detection" }],
          mapProviderConfig as ProviderParams
        );
        setPipeline(newPipeline);
        setStatus({ color: '#4caf50', text: 'AI Model Ready! Draw a polygon to detect oil storage tanks using the controls on the right.' });
        
        // Set up draw event listener after pipeline is ready
        map.current?.on('draw.create', async (e) => {
          console.log('Draw event triggered', e.features[0]);
          setStatus({ color: '#2196f3', text: 'Processing detection...' });
          try {
            // Run inference
            const result = await newPipeline.inference({
              inputs: { polygon: e.features[0] },
              mapSourceParams: { zoomLevel: inferenceZoomLevel }
            });
 
            if (map.current?.getSource('detections')) {
              map.current.removeLayer('detections');
              map.current.removeSource('detections');
            }
 
            map.current?.addSource("detections", {
              type: "geojson",
              data: result.detections,
            });
            map.current?.addLayer({
              id: 'detections',
              type: 'fill',
              source: 'detections',
              paint: { 'fill-color': '#ff0000', 'fill-opacity': 0.5 }
            });
 
            setStatus({
              color: '#4caf50',
              text: `Found ${result.detections.features?.length || 0} oil storage tank${(result.detections.features?.length || 0) !== 1 ? 's' : ''}!`,
            });
          } catch (error) {
            console.error('Detection error:', error);
            setStatus({ color: '#f44336', text: 'Error during detection' });
          }
        });
      } catch (error) {
        console.error('Pipeline initialization error:', error);
        setStatus({ color: '#f44336', text: 'Failed to Initialize Model' });
      }
    })();
 
    return () => map.current?.remove();
  }, []);
 
  const resetMap = () => {
    // Clear drawn features using the draw reference
    drawRef.current?.deleteAll();
    
    // Clear detections
    if (map.current?.getSource('detections')) {
      map.current.removeLayer('detections');
      map.current.removeSource('detections');
    }
    
    setStatus({ color: '#4caf50', text: 'AI Model Ready! Draw a polygon to detect oil storage tanks using the controls on the right.' });
  };
 
  return (
    <div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
      <div style={{ padding: '16px', backgroundColor: status.color, color: 'white', fontSize: '20px', textAlign: 'center', fontWeight: 'bold', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ flex: 1 }}>{status.text}</div>
        {status.text.includes('Found') && (
          <button onClick={resetMap} style={{ padding: '8px 16px', backgroundColor: 'rgba(255,255,255,1)', color: 'black', border: '1px solid white', borderRadius: '4px', cursor: 'pointer', fontSize: '14px', marginLeft: '16px' }}>
            Reset
          </button>
        )}
      </div>
      <div ref={mapContainer} style={{ height: '100%', width: '100%' }} />
    </div>
  );
}
 
export default App;

Step 4: Run Your Application

Start your React development server:

npm start

Your app will open at http://localhost:3000. Draw a polygon on the map and watch the AI detect objects in real-time!

🎉

Congratulations! You now have a working AI-powered mapping application. The AI will analyze satellite imagery within drawn polygons and detect objects like buildings, vehicles, and infrastructure.


For more advanced Web Worker patterns and techniques, see the Web Worker documentation.