Skip to content

Commit

Permalink
Implement support for licensor attributions
Browse files Browse the repository at this point in the history
  • Loading branch information
tawera-manaena committed Oct 16, 2024
1 parent a61b652 commit 2fb2339
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/config-loader/src/json/tiff.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ export async function initImageryFromTiffUrl(
noData: params.noData,
files: params.files,
collection: stac ?? undefined,
providers: stac?.providers,
};
imagery.overviews = await ConfigJson.findImageryOverviews(imagery);
log?.info({ title, imageryName, files: imagery.files.length }, 'Tiff:Loaded');
Expand Down
11 changes: 11 additions & 0 deletions packages/config/src/config/imagery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ export const ConfigImageryOverviewParser = z
})
.refine((obj) => obj.minZoom < obj.maxZoom);

export const ProvidersParser = z.object({
name: z.string(),
description: z.string().optional(),
roles: z.array(z.string()).optional(),
url: z.string().optional(),
});
export const BoundingBoxParser = z.object({ x: z.number(), y: z.number(), width: z.number(), height: z.number() });
export const NamedBoundsParser = z.object({
/**
Expand Down Expand Up @@ -140,6 +146,11 @@ export const ConfigImageryParser = ConfigBase.extend({
* Separate overview cache
*/
overviews: ConfigImageryOverviewParser.optional(),

/**
* list of providers and their metadata
*/
providers: z.array(ProvidersParser).optional(),
});

export type ConfigImagery = z.infer<typeof ConfigImageryParser>;
3 changes: 2 additions & 1 deletion packages/lambda-tiler/src/routes/attribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,14 @@ async function tileSetAttribution(

items.push(item);

const providers = im.providers?.map((p) => ({ ...p, roles: p.roles ?? [] })) ?? getHost(host);
const zoomMin = TileMatrixSet.convertZoomLevel(layer.minZoom ? layer.minZoom : 0, GoogleTms, tileMatrix, true);
const zoomMax = TileMatrixSet.convertZoomLevel(layer.maxZoom ? layer.maxZoom : 32, GoogleTms, tileMatrix, true);
cols.push({
stac_version: Stac.Version,
license: Stac.License,
id: im.id,
providers: getHost(host),
providers,
title,
description: 'No description',
extent,
Expand Down
32 changes: 27 additions & 5 deletions packages/lambda-tiler/src/routes/tile.style.json.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
BasemapsConfigProvider,
ConfigId,
ConfigImagery,
ConfigPrefix,
ConfigTileSetRaster,
Layer,
Expand All @@ -9,7 +10,7 @@ import {
TileSetType,
} from '@basemaps/config';
import { DefaultExaggeration } from '@basemaps/config/build/config/vector.style.js';
import { GoogleTms, Nztm2000QuadTms, TileMatrixSet, TileMatrixSets } from '@basemaps/geo';
import { Epsg, GoogleTms, Nztm2000QuadTms, TileMatrixSet, TileMatrixSets } from '@basemaps/geo';
import { Env, toQueryString } from '@basemaps/shared';
import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
import { URL } from 'url';
Expand Down Expand Up @@ -153,12 +154,13 @@ async function ensureTerrain(
* Generate a StyleJSON from a tileset
* @returns
*/
export function tileSetToStyle(
export async function tileSetToStyle(
req: LambdaHttpRequest<StyleGet>,
config: BasemapsConfigProvider,
tileSet: ConfigTileSetRaster,
tileMatrix: TileMatrixSet,
apiKey: string,
): StyleJson {
): Promise<StyleJson> {
// If the style has outputs defined it has a different process for generating the stylejson
if (tileSet.outputs) return tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey);

Expand All @@ -175,12 +177,32 @@ export function tileSetToStyle(
(Env.get(Env.PublicUrlBase) ?? '') +
`/v1/tiles/${tileSet.name}/${tileMatrix.identifier}/{z}/{x}/{y}.${tileFormat}${query}`;

// attempt to load the tileset's imagery
const imagery = await (function (): Promise<ConfigImagery | null> {
if (tileSet.layers.length !== 1) return Promise.resolve(null);

const imageryId = tileSet.layers[0][Epsg.Nztm2000.code];
if (imageryId === undefined) return Promise.resolve(null);

return config.Imagery.get(imageryId);
})();

// attempt to extract a licensor from the imagery's providers
const licensor = imagery?.providers?.find((p) => p?.roles?.includes('licensor'))?.name;

const styleId = `basemaps-${tileSet.name}`;
return {
id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
name: tileSet.name,
version: 8,
sources: { [styleId]: { type: 'raster', tiles: [tileUrl], tileSize: 256 } },
sources: {
[styleId]: {
type: 'raster',
tiles: [tileUrl],
tileSize: 256,
attribution: licensor ?? undefined,
},
},
layers: [{ id: styleId, type: 'raster', source: styleId }],
};
}
Expand Down Expand Up @@ -248,7 +270,7 @@ async function generateStyleFromTileSet(
throw new LambdaHttpResponse(400, 'Only raster tile sets can generate style JSON');
}
if (tileSet.outputs) return tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey);
else return tileSetToStyle(req, tileSet, tileMatrix, apiKey);
return tileSetToStyle(req, config, tileSet, tileMatrix, apiKey);
}

export interface StyleGet {
Expand Down
13 changes: 10 additions & 3 deletions packages/landing/src/attribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Config } from './config.js';
import { mapToBoundingBox } from './tile.matrix.js';
import { MapOptionType } from './url.js';

const Copyright = ${Stac.License} LINZ`;
const Copyright = ${Stac.License}`;

export class MapAttributionState {
/** Cache the loading of attribution */
Expand Down Expand Up @@ -168,11 +168,18 @@ export class MapAttribution implements maplibre.IControl {
const filteredLayerIds = filtered.map((x) => x.collection.id).join('_');
Config.map.emit('visibleLayers', filteredLayerIds);

const licensor = (function (): string | null {
const providers = filtered[0].collection.providers;
if (providers === undefined) return null;

return providers.find((p) => p.roles.some((r) => r === 'licensor'))?.name ?? null;
})();

let attributionHTML = attr.renderList(filtered);
if (attributionHTML === '') {
attributionHTML = Copyright;
attributionHTML = `${Copyright} ${licensor ?? 'LINZ'}`;
} else {
attributionHTML = Copyright + ' - ' + attributionHTML;
attributionHTML = `${Copyright} ${licensor ?? 'LINZ'} - ${attributionHTML}`;
}

this.setAttribution(attributionHTML);
Expand Down

0 comments on commit 2fb2339

Please sign in to comment.