Skip to content

Commit

Permalink
Add wms image raw implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ger-benjamin committed Feb 13, 2024
1 parent b6b7a2e commit e541fc3
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 70 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# @geoblocks/geoblocks changes

## 0.2.5
- Add **raw** encode support for WMS and Tile WMS layers.

## 0.2.4
- Move createReport to utils.

Expand Down
2 changes: 1 addition & 1 deletion demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ document.querySelector('#print').addEventListener('click', async () => {
*/
const mapSpec = await encoder.encodeMap({
map,
scale: 1,
scale: 10000,
printResolution: 96,
dpi: 254,
customizer: customizer,
Expand Down
237 changes: 168 additions & 69 deletions src/MFPEncoder.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import {getWmtsMatrices, asOpacity, getWmtsUrl} from './utils';
import {getWmtsMatrices, asOpacity, getWmtsUrl, getAbsoluteUrl} from './utils';
import {drawFeaturesToContext, createCoordinateToPixelTransform} from './mvtUtils';

import TileLayer from 'ol/layer/Tile.js';
import WMTSSource from 'ol/source/WMTS.js';
import TileWMSSource from 'ol/source/TileWMS.js';
import OSMSource from 'ol/source/OSM.js';

import {getWidth as getExtentWidth, getHeight as getExtentHeight} from 'ol/extent.js';

import BaseCustomizer from './BaseCustomizer';
import type Map from 'ol/Map.js';
import type {MFPImageLayer, MFPLayer, MFPMap, MFPOSMLayer, MFPWmtsLayer} from './types';
import type {
MFPImageLayer,
MFPLayer,
MFPMap,
MFPOSMLayer,
MFPWmtsLayer,
MFPVectorLayer,
MFPWmsLayer,
} from './types';
import type WMTS from 'ol/source/WMTS.js';

import type {Geometry} from 'ol/geom.js';
import type {State} from 'ol/layer/Layer.js';
import ImageLayer from 'ol/layer/Image.js';
import ImageWMSSource from 'ol/source/ImageWMS.js';
import {toDegrees} from 'ol/math.js';
import VectorTileLayer from 'ol/layer/VectorTile.js';
import VectorLayer from 'ol/layer/Vector.js';
Expand All @@ -37,6 +47,7 @@ export interface EncodeMapOptions {
export default class MFPBaseEncoder {
readonly url: string;
private scratchCanvas: HTMLCanvasElement = document.createElement('canvas');
private printResolution = 96;

/**
*
Expand All @@ -57,7 +68,8 @@ export default class MFPBaseEncoder {
const projection = view.getProjection().getCode();
const rotation = toDegrees(view.getRotation());
const mapLayerGroup = options.map.getLayerGroup();
const layers = await this.encodeLayerGroup(mapLayerGroup, options.printResolution, options.customizer);
this.printResolution = options.printResolution;
const layers = await this.encodeLayerGroup(mapLayerGroup, options.customizer);

return {
center,
Expand All @@ -72,15 +84,10 @@ export default class MFPBaseEncoder {
/**
*
* @param layerGroup The top level layer group of a map
* @param printResolution
* @param customizer
* @return a list of Mapfish print layer specs
*/
async encodeLayerGroup(
layerGroup: LayerGroup,
printResolution: number,
customizer: BaseCustomizer,
): Promise<MFPLayer[]> {
async encodeLayerGroup(layerGroup: LayerGroup, customizer: BaseCustomizer): Promise<MFPLayer[]> {
const layerStates = layerGroup
.getLayerStatesArray()
.filter(customizer.layerFilter)
Expand All @@ -89,8 +96,7 @@ export default class MFPBaseEncoder {

const layers: MFPLayer[] = [];
for (const layerState of layerStates) {
console.assert(printResolution !== undefined);
const spec = await this.encodeLayerState(layerState, printResolution, customizer);
const spec = await this.encodeLayerState(layerState, customizer);
if (spec) {
if (Array.isArray(spec)) {
layers.push(...spec);
Expand All @@ -105,83 +111,114 @@ export default class MFPBaseEncoder {
/**
* Encodes a given OpenLayers layerState to Mapfish print format.
* @param layerState
* @param printResolution
* @param customizer
* @return a spec fragment
*/
async encodeLayerState(
layerState: State,
printResolution: number,
customizer: BaseCustomizer,
): Promise<MFPLayer[] | MFPLayer | null> {
if (
!layerState.visible ||
printResolution < layerState.minResolution ||
printResolution >= layerState.maxResolution
this.printResolution < layerState.minResolution ||
this.printResolution >= layerState.maxResolution
) {
return null;
}
const layer = layerState.layer;

if (layer instanceof VectorTileLayer) {
return await this.encodeMVTLayerState(layerState, printResolution, customizer);
if (layer instanceof ImageLayer) {
return this.encodeImageLayerState(layerState, customizer);
}

if (layer instanceof VectorLayer) {
const encoded = new VectorEncoder(layerState, customizer).encodeVectorLayer(this.printResolution)!;
this.addRenderAsSVG(layerState, encoded);
return encoded;
}

if (layer instanceof TileLayer) {
return this.encodeTileLayerState(layerState, customizer);
} else if (layer instanceof VectorLayer) {
const encoded = new VectorEncoder(layerState, customizer).encodeVectorLayer(printResolution)!;
const renderAsSvg = layer.get('renderAsSvg');
if (renderAsSvg !== undefined) {
encoded.renderAsSvg = renderAsSvg;
}
return encoded;
}

if (layer instanceof VectorTileLayer) {
return await this.encodeMVTLayerState(layerState, customizer);
}

return null;
}

/**
*
* @param layerState An MVT layer state
* @param printResolution
* @param customizer
* @return a spec fragment
* Get "renderAsSvg" to the encoded object if it exists in the layer.
*/
async encodeMVTLayerState(
layerState: State,
printResolution: number,
customizer: BaseCustomizer,
): Promise<MFPLayer[] | MFPLayer | null> {
const layer = layerState.layer as VectorTileLayer;
const {MVTEncoder} = await import('@geoblocks/print');
const encoder = new MVTEncoder();
const printExtent = customizer.getPrintExtent();
const width = getExtentWidth(printExtent) / printResolution;
const height = getExtentHeight(printExtent) / printResolution;
const canvasSize: [number, number] = [width, height];
const printOptions = {
layer,
printExtent: customizer.getPrintExtent(),
tileResolution: printResolution,
styleResolution: printResolution,
canvasSize: canvasSize,
addRenderAsSVG(layerState: State, encoded: MFPVectorLayer) {
const renderAsSvg = layerState.layer.get('renderAsSvg');
if (renderAsSvg !== undefined) {
encoded.renderAsSvg = renderAsSvg;
}
}

/**
* @returns An Encoded WMS Image layer from an Image Layer (high level method).
*/
encodeImageLayerState(layerState: State, customizer: BaseCustomizer): MFPWmsLayer | null {
const layer = layerState.layer;
if (!(layer instanceof ImageLayer)) {
console.assert(layer instanceof ImageLayer);
}
const source = layer.getSource();
if (source instanceof ImageWMSSource) {
return this.encodeImageWmsLayerState(layerState, customizer);
}
return null;
}

/**
* @returns An Encoded WMS Image layer from an Image WMS Source (high level method).
*/
encodeImageWmsLayerState(layerState: State, customizer: BaseCustomizer) {
const layer = layerState.layer;
const source = layer.getSource() as ImageWMSSource;
console.assert(layer instanceof ImageWMSSource);
const url = source.getUrl();
if (url !== undefined) {
return this.encodeWmsLayerState(layerState, url, source.getParams(), customizer);
}
return null;
}

/**
* @returns An Encoded WMS Image layer from an Image WMS Source.
*/
encodeWmsLayerState(layerState: State, url: string, params: any, customizer: BaseCustomizer): MFPWmsLayer {
const layer = layerState.layer;

if (url.startsWith('//')) {
url = window.location.protocol + url;
}
const url_object = new URL(url);

let serverType = undefined;
if (params.SERVERTYPE !== 'arcgis') {
serverType = params.SERVERTYPE;
}

const layers = params.LAYERS instanceof String ? params.LAYERS.split(',') : params.LAYERS;
const styles = params.STYLES instanceof String ? params.STYLES.split(',') : params.STYLES;

return {
name: layer.get('name'),
baseURL: getAbsoluteUrl(url_object.origin + url_object.pathname),
imageFormat: 'FORMAT' in params ? params.FORMAT : 'image/png',
layers,
customParams: {},
serverType,
type: 'wms',
opacity: layer.getOpacity(),
version: params.VERSION,
useNativeAngle: true,
styles: styles || [''],
};
const results = await encoder.encodeMVTLayer(printOptions);
return results
.filter((resut) => resut.baseURL.length > 6)
.map(
(result) =>
Object.assign(
{
type: 'image',
name: layer.get('name'),
opacity: 1,
imageFormat: 'image/png',
},
result,
) as MFPLayer,
);
}

/**
Expand All @@ -190,17 +227,39 @@ export default class MFPBaseEncoder {
* @param customizer
* @return a spec fragment
*/
encodeTileLayerState(layerState: State, customizer: BaseCustomizer): MFPOSMLayer | MFPWmtsLayer {
encodeTileLayerState(
layerState: State,
customizer: BaseCustomizer,
): MFPOSMLayer | MFPWmtsLayer | MFPWmsLayer | null {
const layer = layerState.layer;
console.assert(layer instanceof TileLayer);
const source = layer.getSource();
if (source instanceof WMTSSource) {
return this.encodeTileWmtsLayer(layerState, customizer);
} else if (source instanceof OSMSource) {
return this.encodeTileWmtsLayerState(layerState, customizer);
}
if (source instanceof TileWMSSource) {
return this.encodeTileWmsLayerState(layerState, customizer);
}
if (source instanceof OSMSource) {
return this.encodeOSMLayerState(layerState, customizer);
} else {
return null;
}
return null;
}

/**
* Encodes a tiled WMS layerState as a MFPWmsLayer
* @param layerState
* @param customizer
* @return a spec fragment
*/
encodeTileWmsLayerState(layerState: State, customizer: BaseCustomizer): MFPWmsLayer {
const layer = layerState.layer;
console.assert(layer instanceof TileLayer);
const source = layer.getSource() as TileWMSSource;
console.assert(layer instanceof TileWMSSource);
const urls = source.getUrls();
console.assert(!!urls);
return this.encodeWmsLayerState(layerState, urls[0], source.getParams(), customizer);
}

/**
Expand All @@ -226,7 +285,7 @@ export default class MFPBaseEncoder {
* @param customizer
* @return a spec fragment
*/
encodeTileWmtsLayer(layerState: State, customizer: BaseCustomizer): MFPWmtsLayer {
encodeTileWmtsLayerState(layerState: State, customizer: BaseCustomizer): MFPWmtsLayer {
const layer = layerState.layer;
console.assert(layer instanceof TileLayer);
const source = layer.getSource()! as WMTS;
Expand Down Expand Up @@ -254,6 +313,46 @@ export default class MFPBaseEncoder {
return wmtsLayer;
}

/**
* @param layerState An MVT layer state
* @param customizer
* @return a spec fragment
*/
async encodeMVTLayerState(
layerState: State,
customizer: BaseCustomizer,
): Promise<MFPLayer[] | MFPLayer | null> {
const layer = layerState.layer as VectorTileLayer;
const {MVTEncoder} = await import('@geoblocks/print');
const encoder = new MVTEncoder();
const printExtent = customizer.getPrintExtent();
const width = getExtentWidth(printExtent) / this.printResolution;
const height = getExtentHeight(printExtent) / this.printResolution;
const canvasSize: [number, number] = [width, height];
const printOptions = {
layer,
printExtent: customizer.getPrintExtent(),
tileResolution: this.printResolution,
styleResolution: this.printResolution,
canvasSize: canvasSize,
};
const results = await encoder.encodeMVTLayer(printOptions);
return results
.filter((resut) => resut.baseURL.length > 6)
.map(
(result) =>
Object.assign(
{
type: 'image',
name: layer.get('name'),
opacity: 1,
imageFormat: 'image/png',
},
result,
) as MFPLayer,
);
}

/**
* Encodes Image layerState.
* @param layerState
Expand Down
13 changes: 13 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,19 @@ export interface MFPWmtsLayer extends MFPLayer {
version: string;
}

export interface MFPWmsLayer extends MFPLayer {
type: 'wms';
baseURL: string;
imageFormat: string;
layers: string[];
customParams: Record<string, string>;
serverType: string;
opacity: number;
version: string;
useNativeAngle: boolean;
styles: string[];
}

export interface MFPImageLayer extends MFPLayer {
type: 'image';
extent: number[];
Expand Down

0 comments on commit e541fc3

Please sign in to comment.