Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions src/composables/__tests__/useDownloadGrid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,29 @@ import { getDownloadParquetUrl, DOWNLOAD_GRID_URL } from '../../layers/Download-
import useSettings from '../useSettings'

describe('getDownloadParquetUrl', () => {
it('builds a Source Cooperative URL using year and tile id', () => {
it('builds a Source Cooperative URL using year and tile id, with a year-prefixed filename', () => {
expect(getDownloadParquetUrl(2025, 'N40W100')).toBe(
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/2025/N40W100.parquet',
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/2025/2025_N40W100.parquet',
)
})

it('handles southern/western hemisphere tiles', () => {
expect(getDownloadParquetUrl(2024, 'S03E036')).toBe(
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/2024/S03E036.parquet',
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/2024/2024_S03E036.parquet',
)
})
})

describe('DOWNLOAD_GRID_URL', () => {
it('points to the download-tiles manifest on Source Cooperative', () => {
it('points to the v2 download-tiles manifest on Source Cooperative', () => {
expect(DOWNLOAD_GRID_URL).toBe(
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/ftw-download-grid.geojson',
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/ftw-download-grid-v2.geojson',
)
})
})

describe('featureToGridCell', () => {
it('extracts tile metadata from feature properties', () => {
it('extracts tile metadata from feature properties, including per-year stats dicts', () => {
const feature = new Feature({
geometry: new Polygon([
[
Expand All @@ -44,8 +44,8 @@ describe('featureToGridCell', () => {
lat_min: 40,
lon_min: 0,
years: [2024, 2025],
feature_count: 1234,
size_bytes: 2_500_000,
feature_counts: { '2024': 1234, '2025': 1502 },
size_bytes: { '2024': 2_500_000, '2025': 2_780_000 },
})

const cell = featureToGridCell(feature)
Expand All @@ -54,8 +54,8 @@ describe('featureToGridCell', () => {
lat_min: 40,
lon_min: 0,
years: [2024, 2025],
feature_count: 1234,
size_bytes: 2_500_000,
feature_counts: { '2024': 1234, '2025': 1502 },
size_bytes: { '2024': 2_500_000, '2025': 2_780_000 },
})
})

Expand Down Expand Up @@ -84,7 +84,7 @@ describe('featureToGridCell', () => {
years: [2025],
})
const cell = featureToGridCell(feature)
expect(cell.feature_count).toBeUndefined()
expect(cell.feature_counts).toBeUndefined()
expect(cell.size_bytes).toBeUndefined()
})

Expand Down Expand Up @@ -177,7 +177,7 @@ describe('useDownloadGrid', () => {
expect(handleGridClick(fakeMap, [0, 0])).toBe(true)
expect(clickSpy).toHaveBeenCalled()
expect(anchor.href).toBe(
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/2025/N40W100.parquet',
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/2025/2025_N40W100.parquet',
)
expect(anchor.download).toBe('ftw-fields-N40W100-2025.parquet')
})
Expand Down
20 changes: 16 additions & 4 deletions src/composables/useDownloadGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ export interface GridCell {
lat_min: number
lon_min: number
years: number[]
feature_count?: number
size_bytes?: number
// Per-year dicts keyed by year-as-string (e.g. "2024", "2025"), as served by
// the v2 manifest. Absent years are simply missing keys.
feature_counts?: Record<string, number>
size_bytes?: Record<string, number>
}

const { settings } = useSettings()
Expand All @@ -27,6 +29,16 @@ const DOWNLOAD_GRID_MAX_ZOOM = 8
// Track maps we've already wired up so we don't register duplicate listeners.
const initializedMaps = new WeakSet<Map>()

/** Narrow an unknown value to a year-keyed dict of numbers. */
function toNumericYearDict(value: unknown): Record<string, number> | undefined {
if (!value || typeof value !== 'object') return undefined
const out: Record<string, number> = {}
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
if (typeof v === 'number') out[k] = v
}
return Object.keys(out).length > 0 ? out : undefined
}

/** Convert an OL feature from the grid layer into our GridCell DTO. */
export function featureToGridCell(feature: FeatureLike): GridCell {
const props = feature.getProperties()
Expand All @@ -35,8 +47,8 @@ export function featureToGridCell(feature: FeatureLike): GridCell {
lat_min: typeof props.lat_min === 'number' ? props.lat_min : 0,
lon_min: typeof props.lon_min === 'number' ? props.lon_min : 0,
years: Array.isArray(props.years) ? props.years : [],
feature_count: typeof props.feature_count === 'number' ? props.feature_count : undefined,
size_bytes: typeof props.size_bytes === 'number' ? props.size_bytes : undefined,
feature_counts: toNumericYearDict(props.feature_counts),
size_bytes: toNumericYearDict(props.size_bytes),
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/layers/Download-Grid-Layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { Fill, Stroke, Style } from 'ol/style'
import type { FeatureLike } from 'ol/Feature'

export const DOWNLOAD_GRID_URL =
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/ftw-download-grid.geojson'
'https://data.source.coop/ftw/global-field-boundaries/download-tiles/ftw-download-grid-v2.geojson'

export const getDownloadParquetUrl = (year: number, tileId: string) =>
`https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/${year}/${tileId}.parquet`
`https://data.source.coop/ftw/global-field-boundaries/download-tiles/geoparquet/${year}/${year}_${tileId}.parquet`

const normalStyle = new Style({
fill: new Fill({ color: 'rgba(0, 136, 136, 0.05)' }),
Expand Down
Loading