npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agvmap-react

v0.6.0

Published

A tool designed to visualize geospatial data on a map, integrating custom layers fetched from a REST API, and displaying relevant time-series data. This project is built using React, with `react-map-gl` for map integration.

Readme

Geospatial Data Visualization Tool

A tool designed to visualize geospatial data on a map, integrating custom layers fetched from a REST API, and displaying relevant time-series data. This project is built using React, with react-map-gl for map integration.

Project Setup

To run this project locally, you'll need the following:

  1. MapTiler API Key for basemap style
  2. Sentinel Hub Client ID and Secret for fetching data and imagery from sentinel hubs

the following environment variables are required:

VITE_MAPTILER_ACCESS_KEY=your-maptiler-key
VITE_SENTINAL_HUB_CLIENT_ID=your-sentinal-hub-api-key
VITE_SENTINAL_HUB_CLIENT_SECRET=your-sentinal-hub-client-secret
VITE_SENTINAL_HUB_WMTS_ID=sentinal-hub-wmts-layer-id
VITE_AGV_API_USERNAME=your_username
VITE_AGV_API_PASSWORD=your_password

Steps to Setup:

  1. Rename env.example to .env.
  2. Paste the API keys and credentials into the .env file.
  3. in the project directory, run yarn install in order to install required packages
  4. run yarn dev to start the development server
  5. goto localhost:3000 and you shall see the app running.

USage example


### Composable usage

The default `AgvMap` component remains available. For customized layouts, use
the compound API. The exported map parts automatically use the active map ref
and store state, so they generally do not require internal context wiring:

```jsx
import * as Agvmap from 'agviewermap-react';
import 'agviewermap-react/style.css';

<Agvmap.Root configs={configs} authToken={authToken}>
  <Agvmap.Providers markers={markers}>
    <Agvmap.themeProvider configs={configs}>
      <Agvmap.Map style={style} configs={configs}>
        <Agvmap.NDVILayer />
        <Agvmap.LayerDataViewer />
        <Agvmap.ColorLegend />
        <Agvmap.BottomControls />
        <Agvmap.Markers />
        <Agvmap.MarkerPopup />
        <Agvmap.Farms />
        <Agvmap.Plots />
        <Agvmap.NavbarWrapper />
      </Agvmap.Map>
    </Agvmap.themeProvider>
  </Agvmap.Providers>
</Agvmap.Root>

Passing children to Agvmap.Map opts into a custom map layout. Omitting children preserves the existing complete layout. Agvmap.ThemeProvider is also available as a conventional capitalized alias for themeProvider.

For example, a more complete custom layout can be assembled like this:

import * as Agvmap from 'agviewermap-react';
import 'agviewermap-react/style.css';

function CustomMap({ configs, authToken, markers, style }) {
  return (
    <div style={{ height: '100vh' }}>
      <Agvmap.Root configs={configs} authToken={authToken}>
        <Agvmap.Providers markers={markers}>
          <Agvmap.themeProvider configs={configs}>
            <Agvmap.Map style={style} configs={configs}>
              <Agvmap.MapRasterLayer />
              <Agvmap.NavbarWrapper />
              <Agvmap.MapDateSelector />
              <Agvmap.MapControl />
              <Agvmap.MapPickerControl />
              <Agvmap.LayerDataViewer />
              <Agvmap.ColorLegend />
              <Agvmap.BottomControls />
              <Agvmap.MapEditingControls />
              <Agvmap.Markers />
              <Agvmap.MarkerPopup />
              <Agvmap.MapFarms />
              <Agvmap.MapPlots />
              <Agvmap.StatusBar />
              <Agvmap.MapLoadingOverlay />
              <Agvmap.AreaDetails />
              <Agvmap.ComparisonMap />
            </Agvmap.Map>
          </Agvmap.themeProvider>
        </Agvmap.Providers>
      </Agvmap.Root>
    </div>
  );
}

Important: once children are passed to Agvmap.Map, they replace the default internal layout. Add only the parts your application needs, or use the original AgvMap component when you want the complete built-in experience.

Public hooks for custom components

Custom children can read and control the data used by the map through the public hooks below. They must render inside Agvmap.Providers (normally as a child of Agvmap.Map). Hooks throw a helpful error when used outside the provider tree.

import {
  useMapContext,
  useMapState,
  useFarms,
  usePlots,
  useMarkers,
  useRasterLayer,
} from 'agviewermap-react';

function NavigationAndDataPanel() {
  const { mapRef } = useMapContext();
  const { farms, loading: farmsLoading } = useFarms();
  const { plots, setClickedPlot } = usePlots();
  const { markers } = useMarkers();
  const { layer, opacity, setOpacity } = useRasterLayer();
  const dateRange = useMapState((state) => state.dateRange);

  const map = mapRef.current?.getMap?.();

  return (
    <button onClick={() => map?.fitBounds(/* bounds */)}>
      {farmsLoading ? 'Loading farms…' : `${farms.length} farms`}
    </button>
  );
}

The complete public hook list is: useMapContext, useMapState, useFarms, usePlots, useMarkers, useRasterLayer, useSettings, useInitialView, useDarkMode, useUserPreferences, useConfirm, and useTour.

useMapState(selector) supports Zustand-style selectors, so prefer selecting one field instead of subscribing to the entire store. The returned farm, plot, marker, and raster objects include their current data, loading flags, setters, and supported actions. These hooks are the supported public API; consumers should not import files from the package's internal contexts, hooks, or stores directories.

Hook reference

All data hooks below must be called from a component rendered inside <Agvmap.Providers>. The values update reactively when the map application state changes.

useMapContext() returns the map-level refs and interaction state:

| Property | Description | | --- | --- | | mapRef | Ref to the react-map-gl map wrapper. Use mapRef.current?.getMap() to get the underlying MapLibre instance. | | drawRef | Ref to the Mapbox Draw instance when drawing is enabled. | | mapContainerRef | Ref to the map container element. | | sources | Application-managed map sources. | | setSources(value) | Replace the application-managed sources object. | | mode / setMode(value) | General map interaction mode. | | status / setStatus(value) | Map operation status. | | showDrawActionPopup / setShowDrawActionPopup(value) | Draw-action popup state. | | isDetailActive / setIsDetailActive(value) | Detail-panel state. |

Example:

function RecenterButton({ longitude, latitude, zoom = 12 }) {
  const { mapRef } = useMapContext();

  return (
    <button
      onClick={() => mapRef.current?.getMap()?.flyTo({
        center: [longitude, latitude],
        zoom,
        essential: true,
      })}
    >
      Recenter
    </button>
  );
}

useFarms() returns:

| Property | Description | | --- | --- | | farms | Current farm records. | | farmOptions | Farm options prepared for selectors. | | loading | Whether farm data or an operation is loading. | | refreshFarms() | Reload farms. | | addNewFarm(name, markerSet, seasonStartDate, geometry, weatherStation, unitSystem, preferredLandcoverClass) | Create a farm. Returns a promise. | | handleFarmUpdate(farmId, formValues) | Update a farm. Returns a promise. | | handleDeleteFarm(farmId) | Delete a farm. Returns a promise. | | getFarmsList() | Retrieve the farm list. | | getFarmOptionsList() | Retrieve selector options. |

usePlots() returns:

| Property | Description | | --- | --- | | plots | Current plot records. | | loading | Whether plot data or an operation is loading. | | clickedPlot / setClickedPlot(plot) | Currently selected plot. | | editingPlot | Plot currently being edited. | | showPlots / setShowPlots(value) | Plot visibility. | | addNewPlot(...) | Create a plot. The exact arguments follow the configured plot API. | | handlePlotUpdate(...) | Update a plot. | | handleDeletePlot(plot) | Delete a plot. Returns a promise. | | handleFlyToPlot(coordinates) | Fit the map to plot coordinates. | | handleEditPlot(plot) | Start editing a plot. | | weeksBefore / setWeeksBefore(value) | Historical comparison setting. | | showNdviLayer | Whether the NDVI layer is shown. | | setNDVILayersVisibility(value) | Set NDVI visibility to visible or none. | | toggleNDVILayersVisibility(value?) | Toggle or explicitly set NDVI visibility. | | changeNdviLayerOpacity(value) | Set NDVI opacity from 0 to 100. |

useMarkers() returns:

| Property | Description | | --- | --- | | markers / markersData | Current marker records. | | unfilteredMarkers | Marker records before filters are applied. | | loading | Whether marker data or an operation is loading. | | showMarkers / setShowMarkers(value) | Marker visibility. | | clickedMarker / setClickedMarker(marker) | Currently selected marker. | | markerFilters / setMarkerFilters(value) | Active marker filters. | | resetFilters() | Clear marker filters. | | addNewMarker(...) | Create a marker. | | handleMarkerUpdate(...) | Update a marker. | | handleDeleteMarker(...) | Delete a marker. |

useRasterLayer() returns:

| Property | Description | | --- | --- | | layer / setLayer(value) | Active raster layer option. | | layerOptions | Available raster layer options. | | opacity / setOpacity(value) | Raster opacity, normally 0–100. | | handleOpacityChange(event) | Form-event handler for opacity controls. | | isVisible / setIsVisible(value) | Raster visibility. | | isDetailActive | Raster detail state. | | datesLoading / setDatesLoading(value) | Satellite-date loading state. |

Shared map state

Use useMapState for cross-feature state such as date ranges, view modes, cursor state, selected data, and loading state. The store currently includes these commonly used fields:

| Field | Description | | --- | --- | | dateRange, dateRange2 | Selected date ranges, each with start and end Dates. | | setDateRange(range), setDateRange2(range) | Update date ranges. | | viewMode | Current interaction mode. | | setViewMode(mode) | Set interaction mode. | | mapMode | Normal or comparison map mode. | | rasterLayer, rasterOpacity | Shared raster selection and opacity. | | pickerData, cursorCords | Picker result and pointer coordinates. | | hoveredPlot, hoveredFarmId, clickedMarker | Current hover/selection state. | | sidebarExpanded | Sidebar visibility state. | | loadingNDVIImages, datesLoading, rasterLayerLoading | Loading state. | | configs | Runtime configuration passed to the map. |

The store also provides corresponding setters and mode helpers such as toNormalMode(), toPickerMode(), toDrawMode(), setCursor(), and resetCursor(). Because this store can grow as features are added, consumers should select only the fields they use:

const dateRange = useMapState((state) => state.dateRange);
const setViewMode = useMapState((state) => state.setViewMode);

Settings and supporting hooks

useSettings() returns { settings, setSettings }. settings contains the active basemap configuration and map settings loaded from the backend.

useInitialView() returns either null while settings are loading or an object containing { latitude, longitude, zoom }.

useDarkMode() returns the host system's dark-mode preference as a boolean.

useUserPreferences() returns the persisted user-preference state and actions used by the map. Its exact fields may depend on the configured preferences.

useConfirm() returns the confirmation state tuple:

const [confirm, setConfirm] = useConfirm();
// confirm = { prompt, isOpen, proceed, cancel }

useTour() returns the tour context, including current step, completion and visibility state, and tour navigation actions. It is intended for custom tour controls and integrations.

Data shape and forward compatibility

Farm, plot, and marker records are API/domain objects and may contain fields specific to the deployment. Consumers should use the documented top-level collections and actions, and treat individual record fields as domain data rather than depending on undocumented UI internals. The package's TypeScript declarations expose the hook return values and can be augmented in the host application with its own farm, plot, and marker interfaces.

The following feature components are available for custom layouts:

  • NDVILayer
  • LayerDataViewer
  • ColorLegend
  • BottomControls
  • Markers
  • MarkerPopup
  • Farms
  • Plots
  • NavbarWrapper
  • DateHierarchyDropdownV2
  • PAWStatusPieChart
  • DrawPolygonControl
  • EditPlotGeometryControl
  • AddPlotControl
  • AddStationControl
  • AddFarmModal
  • PickerControl
  • AreaDetails
  • StatusBar
  • MapLoadingOverlay
  • FarmETProgressIndicator
  • MapControl

Self-contained map parts (recommended)

These components wrap the feature parts above and own their positioning and conditional logic — mount them inside Agvmap.Map (or Agvmap.ComparisonMap) and they self-configure to the nearest map instance:

  • MapRasterLayer — raster/NDVI layer (showCroppedImages gating + store dateRange)
  • MapDateSelector — top-left date selector (variant="single" | "split")
  • MapControl — right-side control stack (zoom, fullscreen, split view, draw, picker)
  • MapPickerControl — floating picker toggle (shown in comparison view)
  • MapEditingControls — drawing/editing overlays by viewMode
  • MapPlots / MapFarms — plots and farms bound to the nearest map
  • ComparisonMap — self-contained split/comparison second map

Most take an optional variant prop: "single" (default, binds to dateRange) or "split" (binds to dateRange2). All are exported from the package root.

Map parts entry (agvmap-react/parts)

For consumer systems that want to include only the pieces they need, the package exposes a curated agvmap-react/parts entry. It exports every mountable map component — self-contained wrappers, positioning wrappers, and context-connected leaf renderers — so you can compose a map layout from just the parts your application needs.

import {
  MapControl,
  MapDateSelector,
  MapPlots,
  Markers,
} from 'agvmap-react/parts';

When you pass children to Agvmap.Map, they replace the default internal layout, so you control exactly what renders:

import * as Agvmap from 'agviewermap-react';
import {
  MapControl,
  MapPlots,
  MapDateSelector,
  Markers,
} from 'agviewermap-react/parts';

// Example 1 — a minimal map with just drawing capability
function DrawOnlyMap({ configs }) {
  return (
    <Agvmap.Map style={{ height: '100%' }} configs={configs}>
      <MapControl /> {/* zoom, draw, picker toolbar */}
      <Agvmap.MapEditingControls /> {/* edit/add plot & station overlays */}
    </Agvmap.Map>
  );
}

// Example 2 — a map focused on markers only
function MarkersOnlyMap({ configs, markers }) {
  return (
    <Agvmap.Map style={{ height: '100%' }} configs={configs}>
      <Agvmap.Markers />
      <Agvmap.MarkerPopup />
    </Agvmap.Map>
  );
}

// Example 3 — full custom layout with date navigation and plots
function FullCustomMap({ configs }) {
  return (
    <Agvmap.Map style={{ height: '100%' }} configs={configs}>
      <Agvmap.MapRasterLayer />
      <Agvmap.MapDateSelector />
      <MapControl />
      <Agvmap.MapPlots />
      <Agvmap.MapFarms />
      <Agvmap.StatusBar />
      <Agvmap.MapLoadingOverlay />
    </Agvmap.Map>
  );
}

Note: Agvmap.Map itself must wrap your parts so the required providers (Agvmap.Root, Agvmap.Providers, Agvmap.themeProvider) are mounted. Agvmap.ComparisonMap can be mounted inside as the split-view second map.

The parts entry is type-safe: agvmap-react/parts resolves to types/parts.d.ts, which re-exports the same component types as the package root.

The complete layout can be rebuilt from the exported parts, including DateHierarchyDropdownV2, PAWStatusPieChart, DrawPolygonControl, EditPlotGeometryControl, AddPlotControl, AddStationControl, AddFarmModal, PickerControl, AreaDetails, StatusBar, MapLoadingOverlay, FarmETProgressIndicator, and MapControl. <AgvMap configs={{ VITE_MAPTILER_ACCESS_KEY: your-value, VITE_SENTINAL_HUB_CLIENT_ID: your-value, VITE_SENTINAL_HUB_CLIENT_SECRET: your-value, VITE_SENTINAL_HUB_WMTS_ID: your-value, }}

requestHeaders={{ Authentication: your-value }}


## Project Goal

Implement a map component with custom layers fetched from a REST API, to be integrated into a dashboard for visualization and analysis.

## Observations

- A marker may belong to a farm, and a farm can have multiple markers.
- a farm can have many plots

## FAQs

- **Why does the backend throw a CORS error?**
  - Only port 3000 is allowed on the backend.
- **What is the difference between forecast markers and station markers?**
  - If a marker is associated with a device, it represents a station marker. Forecast markers, on the other hand, are not associated with devices, and their data is fetched from a third-party service.