Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geostyler (phase 1) #1708

Open
wants to merge 8 commits into
base: next
Choose a base branch
from
Open
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
16,560 changes: 8,960 additions & 7,600 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
"@angular/router": "^18.2.1",
"core-js": "^3.32.2",
"file-saver": "^2.0.2",
"geostyler-legend": "^5.0.1",
"geostyler-openlayers-parser": "^5.0.0",
"hammerjs": "2.0.8",
"html2canvas": "^1.4.1",
"jspdf": "^2.5.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/geo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
"@igo2/core": "*",
"@igo2/utils": "*",
"@mat-datetimepicker/core": "~14.0.0",
"geostyler-legend": "^5.0.1",
"geostyler-openlayers-parser": "^5.0.0",
"file-saver": "^2.0.2",
"flexsearch": "0.7.21",
"html2canvas": "^1.4.1",
Expand Down
50 changes: 45 additions & 5 deletions packages/geo/src/lib/layer/shared/layer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { StyleLike as OlStyleLike } from 'ol/style/Style';

import { stylefunction } from 'ol-mapbox-style';
import { Observable, combineLatest, of } from 'rxjs';
import { catchError, concatMap, map } from 'rxjs/operators';
import { catchError, concatMap, map, mergeMap } from 'rxjs/operators';

import { DataSourceService } from '../../datasource/shared/datasource.service';
import {
Expand All @@ -33,6 +33,7 @@ import {
} from '../../datasource/shared/datasources';
import { LayerDBService } from '../../offline/layerDB/layerDB.service';
import { GeoNetworkService } from '../../offline/shared/geo-network.service';
import { GeostylerService } from '../../style/geostyler-service/geostyler.service';
import { StyleService } from '../../style/style-service/style.service';
import { computeMVTOptionsOnHover } from '../utils/layer.utils';
import {
Expand Down Expand Up @@ -60,7 +61,8 @@ export class LayerService {
private messageService: MessageService,
@Optional() private geoNetworkService?: GeoNetworkService,
@Optional() private layerDBService?: LayerDBService,
@Optional() private authInterceptor?: AuthInterceptor
@Optional() private authInterceptor?: AuthInterceptor,
@Optional() private geostylerStyleService?: GeostylerService
) {}

createLayer(layerOptions: AnyLayerOptions): Layer {
Expand Down Expand Up @@ -122,7 +124,7 @@ export class LayerService {
return new Observable((d) => d.next(this.createLayer(layerOptions)));
}

return this.dataSourceService
const datasource$ = this.dataSourceService
.createAsyncDataSource(layerOptions.sourceOptions, detailedContextUri)
.pipe(
map((source) => {
Expand All @@ -132,6 +134,38 @@ export class LayerService {
return this.createLayer(Object.assign(layerOptions, { source }));
})
);

const stylableLayerOptions = layerOptions as
| VectorLayerOptions
| VectorTileLayerOptions;
const geostylerStyleGlobal =
stylableLayerOptions.igoStyle?.geostylerStyle?.global;

if (geostylerStyleGlobal) {
return combineLatest([
this.geostylerStyleService.geostylerToOl(geostylerStyleGlobal),
this.geostylerStyleService.geostylerStyleToLegend(geostylerStyleGlobal)
]).pipe(
mergeMap((res) => {
const writeStyleResult = res[0];
const legend = res[1];
if (writeStyleResult?.output) {
stylableLayerOptions.style = writeStyleResult.output;
}
if (legend) {
if (layerOptions.legendOptions) {
layerOptions.legendOptions.html = legend;
} else {
layerOptions.legendOptions = { html: legend };
}
}

return datasource$;
})
);
} else {
return datasource$;
}
}

private createImageLayer(layerOptions: ImageLayerOptions): ImageLayer {
Expand Down Expand Up @@ -257,7 +291,13 @@ export class LayerService {
if (layerOptions[legacyOption]) {
let newKey = legacyOption;
if (legacyOption === 'style') {
if (layerOptions[legacyOption] instanceof olStyle.Style) {
if (
layerOptions[legacyOption] instanceof olStyle.Style ||
(Array.isArray(layerOptions[legacyOption]) &&
layerOptions[legacyOption].every(
(r) => r instanceof olStyle.Style
))
) {
return;
}
if (typeof layerOptions[legacyOption] === 'object') {
Expand All @@ -281,7 +321,7 @@ export class LayerService {
private createVectorTileLayer(
layerOptions: VectorTileLayerOptions
): VectorTileLayer {
let style: Style[] | Style | OlStyleLike;
let style: Style[] | Style | OlStyleLike = layerOptions.style;
let igoLayer: VectorTileLayer;

if (!layerOptions.igoStyle) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum GeostylerLegendType {
SVG = 'svg',
URL = 'url'
}
159 changes: 159 additions & 0 deletions packages/geo/src/lib/style/geostyler-service/geostyler.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Injectable } from '@angular/core';

import { LegendRenderer } from 'geostyler-legend';
import { OlStyleParser as OpenLayersParser } from 'geostyler-openlayers-parser';
import { IconSymbolizer, Style, WriteStyleResult } from 'geostyler-style';
import { Observable, from, map, tap } from 'rxjs';

import { StyleSourceType } from '../shared';
import { GeostylerLegendType } from './geostyler.enum';

@Injectable({
providedIn: 'root'
})
export class GeostylerService {
/**
* Create a style based on a object as follow.
* Due to the legend renderer limitation, the symbolizers have a size limit in the legend.
* The legend will not affect the symbolizer size data, only on the display will it hava a limit.
*
* ## Feature styles
*
* {
* "name": "GeoStyler Test",
* "symbolizers":[
* {
* "kind": "Fill",
* "color": "#ff0000",
* "width": 5
* }
* ],
* "scaleDenominator": {
* "min": 50,
* "max": 200
* }
* }
*
* @param Observable geostyler WriteStyleResult
* @returns
*/
public geostylerToOl(options: Style): Observable<WriteStyleResult> {
return this.geostylerTo(options, StyleSourceType.OpenLayers);
}

private geostylerTo(
options: Style,
destStyle: StyleSourceType
): Observable<WriteStyleResult> {
let parser;
if (destStyle === StyleSourceType.OpenLayers) {
parser = new OpenLayersParser();
}
if (parser) {
return from(parser.writeStyle(options)).pipe(
tap((res) => this.handleWarningsAndError(res))
);
}
}

public geostylerStyleToLegend(
style: Style,
type: GeostylerLegendType = GeostylerLegendType.SVG,
width?: number,
height?: number
): Observable<string> {
return this.geostylerStylesToLegend([style], type, width, height);
}

public geostylerStylesToLegend(
styles: Style[],
type: GeostylerLegendType = GeostylerLegendType.SVG,
width?: number,
height?: number
): Observable<string> {
const layerDescriptors = this.transferLayersToLegend(styles);

const nbOfRules = styles.reduce(
(partialSum, style) => partialSum + style.rules.length,
0
);
const nbOfNames = styles.reduce(
(partialSum, style) => (partialSum + style.name?.length ? 1 : 0),
0
);
const heightByName = 30;
const heightByRule = 30;

const computedHeightByStyles =
nbOfNames * heightByName + nbOfRules * heightByRule;

const renderer = new LegendRenderer({
maxColumnWidth: 300,
overflow: 'auto',
styles: layerDescriptors,
size: [width ?? 300, height ?? computedHeightByStyles],
hideRect: true
});
return from(renderer.renderAsImage('svg')).pipe(
map((r: Element) => {
const serializer = new XMLSerializer();
const svgXmlString = serializer.serializeToString(r);
if (type === GeostylerLegendType.SVG) {
return svgXmlString;
} else {
const blob = new Blob([svgXmlString], {
type: 'image/svg+xml'
});
const urlCreator = window.URL;
return urlCreator.createObjectURL(blob);
}
})
);
}

private transferLayersToLegend(styles: Style[]): Style[] {
const _styles = [...styles];
const layerDescriptorsList: Style[] = [];
_styles.forEach((_stylesItem) => {
const descriptorLayerRulesAdapted = [];
const descriptorLayerName = _stylesItem.name;
_stylesItem.rules.map((styleRule) => {
const styleRuleSymbolizersAdapted = [];
styleRule.symbolizers.map((styleRuleSymbolizer) => {
switch (styleRuleSymbolizer.kind) {
case 'Icon':
(styleRuleSymbolizer as IconSymbolizer).size = 15;
break;
default:
break;
}
styleRuleSymbolizersAdapted.push(styleRuleSymbolizer);
});
descriptorLayerRulesAdapted.push({
name: styleRule.name,
symbolizers: styleRuleSymbolizersAdapted.filter(
(s) => s.kind !== 'Text'
)
});
});
const styleNoRadius: Style = {
name: descriptorLayerName,
rules: descriptorLayerRulesAdapted
};
layerDescriptorsList.push(styleNoRadius);
});
return layerDescriptorsList;
}

private handleWarningsAndError(writeStyleResult: WriteStyleResult) {
if (writeStyleResult?.warnings) {
console.warn(writeStyleResult.warnings);
}
if (writeStyleResult?.errors) {
console.error(writeStyleResult.errors);
}
if (writeStyleResult?.unsupportedProperties) {
console.warn(writeStyleResult.unsupportedProperties);
}
}
}
1 change: 1 addition & 0 deletions packages/geo/src/lib/style/geostyler-service/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './geostyler.service';
1 change: 1 addition & 0 deletions packages/geo/src/lib/style/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './shared';
export * from './style-service';
export * from './style-list';
export * from './style-modal';
export * from './geostyler-service';
1 change: 1 addition & 0 deletions packages/geo/src/lib/style/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './datasource';
export * from './vector';
export * from './overlay';
export * from './feature';
export * from './style.enum';
3 changes: 3 additions & 0 deletions packages/geo/src/lib/style/shared/style.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum StyleSourceType {
OpenLayers = 'ol'
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import olFeature from 'ol/Feature';
import type { default as OlGeometry } from 'ol/geom/Geometry';
import olStyle from 'ol/style/Style';

import { Style as GeostylerStyle } from 'geostyler-style';

import type { Feature } from '../../../feature/shared/feature.interfaces';

export interface FeatureCommonVectorStyleOptions
Expand All @@ -18,6 +20,11 @@ export interface IgoStyleBase {
igoStyleObject?: Record<string, any>;
mapboxStyle?: MapboxStyle;
styleByAttribute?: StyleByAttribute;
geostylerStyle?: GeostylerStyleInterfaceOptions;
}

export interface GeostylerStyleInterfaceOptions {
global?: GeostylerStyle;
}

export interface OverlayStyleOptions {
Expand Down
3 changes: 2 additions & 1 deletion packages/geo/src/lib/style/style.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ModuleWithProviders, NgModule } from '@angular/core';

import { GeostylerService } from './geostyler-service/geostyler.service';
import { IgoStyleListModule } from './style-list/style-list.module';
import { StyleModalDrawingComponent } from './style-modal/drawing/style-modal-drawing.component';
import { StyleModalLayerButtonComponent } from './style-modal/layer-button/style-modal-layer-button.component';
Expand All @@ -25,7 +26,7 @@ export class IgoStyleModule {
static forRoot(): ModuleWithProviders<IgoStyleModule> {
return {
ngModule: IgoStyleModule,
providers: [StyleService, DrawStyleService]
providers: [StyleService, DrawStyleService, GeostylerService]
};
}
}
Loading