# 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`.

`Agvmap.Map` and the default `Agvmap.AgvMap` accept `zoomOnScroll`. It defaults
to `false`, so wheel zoom requires Ctrl/Cmd as before. Set it to `true` to let
plain mouse-wheel or trackpad scrolling zoom the map (Ctrl/Cmd+scroll continues
to work in either mode).

```jsx
// AGVet: enable plain wheel/trackpad zoom.
<Agvmap.Map configs={configs} zoomOnScroll={true} />

// AGViewer: require Ctrl/Cmd + scroll (the default).
<Agvmap.Map configs={configs} zoomOnScroll={false} />

// The complete component supports the same prop.
<Agvmap.AgvMap configs={configs} zoomOnScroll={true} />
```

The prop can change after mount and applies to the built-in comparison map.
Wheel zoom is available once the map instance exists, without waiting for all
styles or tiles to finish loading. Zoom button tooltips show `⌘ + Scroll` on
macOS and `Ctrl + Scroll` on other platforms: scroll up to zoom in, and scroll
down to zoom out.


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

```jsx
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.

```jsx
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:

```jsx
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:

```jsx
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:

```jsx
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.

```js
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:

```jsx
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.
```
