Skip to content

Commit

Permalink
feat(datasource): add Carto capabilities from mapId, add legends for …
Browse files Browse the repository at this point in the history
…Carto and ArcGIS Rest
  • Loading branch information
MohammedBouheraoua authored and mbarbeau committed Dec 3, 2018
1 parent dc9783e commit d908484
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 35 deletions.
95 changes: 73 additions & 22 deletions projects/geo/src/lib/datasource/shared/capabilities.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable, forkJoin } from 'rxjs';
import { map } from 'rxjs/operators';
import { Observable, forkJoin, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

import { WMSCapabilities, WMTSCapabilities } from 'ol/format';
import { optionsFromCapabilities } from 'ol/source/WMTS.js';
Expand All @@ -13,6 +13,7 @@ import { EsriStyleGenerator } from '../utils/esri-style-generator';
import {
WMTSDataSourceOptions,
WMSDataSourceOptions,
CartoDataSourceOptions,
ArcGISRestDataSourceOptions,
TileArcGISRestDataSourceOptions
} from './datasources';
Expand Down Expand Up @@ -57,29 +58,55 @@ export class CapabilitiesService {
return options;
}

getArcgisOptions(
baseOptions: ArcGISRestDataSourceOptions
): Observable<ArcGISRestDataSourceOptions> {
const baseUrl = baseOptions.url + '/' + baseOptions.layer + '?f=json';
getCartoOptions(
baseOptions: CartoDataSourceOptions
): Observable<CartoDataSourceOptions> {
const baseUrl =
'https://' +
baseOptions.account +
'.carto.com/api/v2/viz/' +
baseOptions.mapId +
'/viz.json';

return this.http
.get(baseUrl)
.jsonp(baseUrl, 'callback')
.pipe(
map((arcgisOptions: any) =>
this.parseArcgisOptions(baseOptions, arcgisOptions)
map((cartoOptions: any) =>
this.parseCartoOptions(baseOptions, cartoOptions)
)
);
}

getArcgisOptions(
baseOptions: ArcGISRestDataSourceOptions
): Observable<ArcGISRestDataSourceOptions> {
const baseUrl = baseOptions.url + '/' + baseOptions.layer + '?f=json';
const modifiedUrl = baseOptions.url.replace('FeatureServer', 'MapServer');
const legendUrl = modifiedUrl + '/legend?f=json';
const arcgisOptions = this.http.get(baseUrl);
const legend = this.http.get(legendUrl).pipe(
map((res: any) => res),
catchError(err => {
console.log('No legend associated with this Feature Service');
return of(err);
})
);
return forkJoin([arcgisOptions, legend]).pipe(
map((res: any) => {
return this.parseArcgisOptions(baseOptions, res[0], res[1]);
})
);
}

getTileArcgisOptions(
baseOptions: TileArcGISRestDataSourceOptions
): Observable<TileArcGISRestDataSourceOptions> {
const baseUrl = baseOptions.url + '/' + baseOptions.layer + '?f=json';
const legendUrl = baseOptions.url + '/legend?f=json';
const tileArcgisOptions = this.http.get(baseUrl);
const arcgisOptions = this.http.get(baseUrl);
const legendInfo = this.http.get(legendUrl);

return forkJoin([tileArcgisOptions, legendInfo]).pipe(
return forkJoin([arcgisOptions, legendInfo]).pipe(
map((res: any) =>
this.parseTileArcgisOptions(baseOptions, res[0], res[1])
)
Expand Down Expand Up @@ -136,7 +163,8 @@ export class CapabilitiesService {
const abstract = layer.Abstract ? layer.Abstract : undefined;
const keywordList = layer.KeywordList ? layer.KeywordList : undefined;
const timeFilter = this.getTimeFilter(layer);
const timeFilterable = timeFilter && Object.keys(timeFilter).length > 0 ? true : false;
const timeFilterable =
timeFilter && Object.keys(timeFilter).length > 0 ? true : false;

const options: WMSDataSourceOptions = ObjectUtils.removeUndefined({
_layerOptionsFromCapabilities: {
Expand All @@ -163,18 +191,39 @@ export class CapabilitiesService {
baseOptions: WMTSDataSourceOptions,
capabilities: any
): WMTSDataSourceOptions {
const options = optionsFromCapabilities(
capabilities,
baseOptions
);
const options = optionsFromCapabilities(capabilities, baseOptions);

return Object.assign(options, baseOptions);
}

private parseCartoOptions(
baseOptions: CartoDataSourceOptions,
cartoOptions: any
): CartoDataSourceOptions {
const layers = [];
const params = cartoOptions.layers[1].options.layer_definition;
params.layers.forEach(element => {
layers.push({
type: element.type.toLowerCase(),
options: element.options,
legend: element.legend
});
});
const options = ObjectUtils.removeUndefined({
config: {
version: params.version,
layers: layers
}
});
return ObjectUtils.mergeDeep(options, baseOptions);
}

private parseArcgisOptions(
baseOptions: ArcGISRestDataSourceOptions,
arcgisOptions: any
arcgisOptions: any,
legend?: any
): ArcGISRestDataSourceOptions {
const legendInfo = legend.layers ? legend : undefined;
const styleGenerator = new EsriStyleGenerator();
const units = arcgisOptions.units === 'esriMeters' ? 'm' : 'degrees';
const style = styleGenerator.generateStyle(arcgisOptions, units);
Expand All @@ -200,6 +249,7 @@ export class CapabilitiesService {
const params = Object.assign(
{},
{
legendInfo: legendInfo,
style: style,
timeFilter: timeFilter,
timeExtent: timeExtent,
Expand All @@ -214,15 +264,16 @@ export class CapabilitiesService {

private parseTileArcgisOptions(
baseOptions: TileArcGISRestDataSourceOptions,
tileArcgisOptions: any,
legendInfo: any
arcgisOptions: any,
legend: any
): TileArcGISRestDataSourceOptions {
const legendInfo = legend.layers ? legend : undefined;
const attributions = new olAttribution({
html: tileArcgisOptions.copyrightText
html: arcgisOptions.copyrightText
});
let timeExtent, timeFilter;
if (tileArcgisOptions.timeInfo) {
const time = tileArcgisOptions.timeInfo.timeExtent;
if (arcgisOptions.timeInfo) {
const time = arcgisOptions.timeInfo.timeExtent;
timeExtent = time[0] + ',' + time[1];
const min = new Date();
min.setTime(time[0]);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';

import { CapabilitiesService } from './capabilities.service';
import { DataSourceService } from './datasource.service';
Expand All @@ -8,7 +8,7 @@ import { WFSService } from './datasources/wfs.service';
describe('DataSourceService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
imports: [HttpClientModule, HttpClientJsonpModule],
providers: [CapabilitiesService, DataSourceService, WFSService]
});
});
Expand Down
7 changes: 7 additions & 0 deletions projects/geo/src/lib/datasource/shared/datasource.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ export class DataSourceService {
private createCartoDataSource(
context: CartoDataSourceOptions
): Observable<CartoDataSource> {
if (context.mapId) {
return this.capabilitiesService
.getCartoOptions(context)
.pipe(
map((options: CartoDataSourceOptions) => new CartoDataSource(options))
);
}
return new Observable(d => d.next(new CartoDataSource(context)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import { FeatureDataSourceOptions } from './feature-datasource.interface';
export interface ArcGISRestDataSourceOptions
extends DataSourceOptions,
FeatureDataSourceOptions {
// type?: 'arcgisrest';
// type?: 'arcgisrest'
layer: string;
params?: ArcGISRestDataSourceOptionsParams;
}

export interface ArcGISRestDataSourceOptionsParams {
customParams?: string[]; // any ArcGIS Rest query parameters for feature service layer resource
legendInfo?: any;
style?: any;
timefilter?: any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as olloadingstrategy from 'ol/loadingstrategy';
import { uuid } from '@igo2/utils';

import { DataSource } from './datasource';
import { DataSourceLegendOptions } from './datasource.interface';
import { ArcGISRestDataSourceOptions } from './arcgisrest-datasource.interface';

export class ArcGISRestDataSource extends DataSource {
Expand Down Expand Up @@ -44,6 +45,11 @@ export class ArcGISRestDataSource extends DataSource {
const time = `time=${this.options.params.timeExtent}`;
params.push(time);
}
if (this.options.params.customParams) {
this.options.params.customParams.forEach(element => {
params.push(element);
});
}
return `${baseUrl}?${params.join('&')}`;
}.bind(this),
strategy: olloadingstrategy.bbox
Expand All @@ -53,4 +59,32 @@ export class ArcGISRestDataSource extends DataSource {
protected generateId() {
return uuid();
}

getLegend(): DataSourceLegendOptions[] {
const legendInfo = this.options.params.legendInfo;
const legend = super.getLegend();
if (legendInfo === undefined || legend.length > 0) {
return legend;
}
const id = parseInt(this.options.layer, 10);
const lyr = legendInfo.layers[id];
let htmlString = '<table><tr><td>' + lyr.layerName + '</td></tr>';

for (let i = 0; i < lyr.legend.length; i++) {
const modifiedUrl = this.options.url.replace(
'FeatureServer',
'MapServer'
);
const src = `${modifiedUrl}/${lyr.layerId}/images/${lyr.legend[i].url}`;
const label = lyr.legend[i].label.replace('<Null>', 'Null');
htmlString +=
`<tr><td align='left'><img src="` +
src +
`" alt ='' /></td><td>` +
label +
'</td></tr>';
}
htmlString += '</table>';
return [{ html: htmlString }];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { DataSourceOptions } from './datasource.interface';

export interface CartoDataSourceOptions extends DataSourceOptions {
// type?: 'carto';
account: string;
mapId?: string; // Fetch the config of a public map from the Carto API using the mapId.

params?: any;
queryPrecision?: string;

crossOrigin?: string;
projection?: string;
config?: any;
map?: string;
account?: string;

ol?: olSourceCarto;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import olSourceCarto from 'ol/source/CartoDB';

import { uuid } from '@igo2/utils';

import { DataSource } from './datasource';
import { DataSourceLegendOptions } from './datasource.interface';
import { CartoDataSourceOptions } from './carto-datasource.interface';
Expand All @@ -26,10 +27,95 @@ export class CartoDataSource extends DataSource {
}

protected createOlSource(): olSourceCarto {
return new olSourceCarto(this.options);
const crossOrigin = this.options.crossOrigin
? this.options.crossOrigin
: 'Anonymous';
const sourceOptions = Object.assign(
{
crossOrigin: crossOrigin
},
this.options
);
return new olSourceCarto(sourceOptions);
}

protected generateId() {
return uuid();
}

getLegend(): DataSourceLegendOptions[] {
const legend = super.getLegend();
if (legend.length > 0) {
return legend;
}
let htmlString = '<table>';
if (this.options.config.layers[0].legend != null) {
this.options.config.layers[0].legend.items.forEach(f => {
if (f['visible'] === true) {
htmlString +=
'<tr><td>' +
'<p><font size="5" color="' +
f['value'] +
'"> &#9679</font></p></td>' +
'<td>' +
f['name'] +
'</td></tr>';
}
});
htmlString += '</table>';
return [{ html: htmlString }];
} else {
// Try to build the legend from the cartocss options
const layerOptions = this.options.config.layers[0].options;
// All available cartocss style options
const types = [
'polygon-fill:',
'marker-fill:',
'shield-fill:',
'building-fill:',
'line-color:'
];
for (let i = 0; i < types.length; i++) {
if (layerOptions.cartocss.includes(types[i])) {
const type = layerOptions.cartocss.split(types[i]).pop();
const color = type.substr(0, type.indexOf(';'));
if (color.includes('ramp')) {
const colors = color.split(', (')[1].split(',');
const data = color.split(', (')[2].split(',');
for (let j = 0; j < colors.length; j++) {
colors[j] = colors[j].replace(/("|\))/g, '');
data[j] = data[j].replace(/("|\))/g, '');
if (data[j].replace(/\s+/g, '') === '=') {
data[j] = 'Autres';
}
htmlString +=
'<tr><td>' +
'<p><font size="5" color="' +
colors[j] +
'"> &#9679</font></p></td>' +
'<td>' +
data[j] +
'</td></tr>';
}
break;
} else {
const title = layerOptions.layer_name
? layerOptions.layer_name
: '';
htmlString +=
'<tr><td>' +
'<p><font size="5" color="' +
color +
'"> &#9679</font></p>' +
'</td><td>' +
title +
'</td></tr>';
break;
}
}
}
htmlString += '</table>';
return [{ html: htmlString }];
}
}
}
Loading

0 comments on commit d908484

Please sign in to comment.