Add styles cartography for catalgo maps

pull/15936/head
Jesús Arroyo Torrens 4 years ago
parent c14bf22836
commit 4e87720515

@ -2,7 +2,7 @@
<div class="base-map">
<div id="map"></div>
<canvas id="deck-canvas"></canvas>
<div v-show="showInfo" class="map-info">
<div v-show="showInfo && description" class="map-info">
<p class="is-small">{{ description }}</p>
</div>
<div v-if="recenter" class="recenter" @click="recenterMap">
@ -18,7 +18,16 @@ import mapboxgl from 'mapbox-gl';
import { Deck } from '@deck.gl/core';
import { CartoBQTilerLayer, BASEMAP } from '@deck.gl/carto';
import colorBinsStyle from './map-styles/color-bins-style';
import colorCategoriesStyle from './map-styles/color-categories-style';
let deck;
let propId;
let colorStyle;
let getFillColor;
let getLineColor;
let pointRadiusMinPixels;
let lineWidthMinPixels;
export default {
name: 'BaseMap',
@ -30,6 +39,7 @@ export default {
return {
map: null,
variable: null,
geomType: null,
initialViewState: {
latitude: 0,
longitude: 0,
@ -49,9 +59,6 @@ export default {
},
description () {
return this.variable && this.variable.description;
},
defaultSource () {
return this.dataset.sample_info && this.dataset.sample_info.default_source || 'Test';
}
},
created () {
@ -74,38 +81,9 @@ export default {
this.syncMapboxViewState(viewState);
},
controller: true,
layers: [
new CartoBQTilerLayer({
data: this.tilesetSampleId(this.dataset.id),
credentials: {
username: 'public',
apiKey: 'default_public',
mapsUrl: 'https://maps-api-v2.carto-staging.com/user/{user}'
},
getFillColor: [130, 109, 186],
getLineColor: [0, 0, 0, 100],
lineWidthMinPixels: 0.5,
getLineWidth: 4,
getRadius: 16,
pickable: true,
onDataLoad: (tileJSON) => {
console.log('TILEJSON', tileJSON);
const { center, tilestats } = tileJSON;
this.initialViewState = {
zoom: parseFloat(center[2]) - 1,
latitude: parseFloat(center[1]),
longitude: parseFloat(center[0]),
bearing: 0,
pitch: 0
};
this.recenterMap();
this.setVariable(tilestats.layers[0].attributes[1]);
}
})
],
getTooltip: ({ object }) => {
if (!object) return false;
const title = this.variable.name || this.variable.attribute;
if (!object || !this.variable) return false;
const title = this.variable.attribute;
let value = object.properties[this.variable.attribute];
if (value === undefined) return false;
if (typeof value === 'number') {
@ -126,6 +104,8 @@ export default {
return { html, style };
}
});
this.renderLayer();
},
methods: {
importMapboxStyles () {
@ -159,7 +139,47 @@ export default {
deck.setProps({ initialViewState: { ...this.initialViewState } });
this.syncMapboxViewState(this.initialViewState);
},
setVariable (variable) {
renderLayer () {
const layers = [
new CartoBQTilerLayer({
data: this.tilesetSampleId(this.dataset.id),
credentials: {
username: 'public',
apiKey: 'default_public',
mapsUrl: 'https://maps-api-v2.carto-staging.com/user/{user}'
},
getFillColor,
getLineColor,
pointRadiusMinPixels,
lineWidthMinPixels,
pickable: true,
onDataLoad: (tileJSON) => {
console.log('TILEJSON', tileJSON);
const { center, tilestats } = tileJSON;
this.initialViewState = {
zoom: parseFloat(center[2]) - 1,
latitude: parseFloat(center[1]),
longitude: parseFloat(center[0]),
bearing: 0,
pitch: 0
};
this.recenterMap();
this.setGeomType(tilestats);
this.setVariable(tilestats);
this.resetColorStyle();
this.generateColorStyle();
this.renderLayer();
}
})
];
deck.setProps({ layers });
},
setGeomType (tilestats) {
this.geomType = tilestats.layers[0].geometry;
},
setVariable (tilestats) {
const variable = tilestats.layers[0].attributes[1];
if (!this.variables || !variable) return;
const variableExtra = this.variables.find((v) => {
return v.id.split('.').slice(-1)[0] === variable.attribute;
});
@ -177,6 +197,91 @@ export default {
});
}
return value.toLocaleString();
},
generateColorStyle () {
const g = this.geomType;
const v = this.variable && this.variable.type;
const stats = this.variable;
propId = this.variable && this.variable.attribute;
if (g === 'Polygon' && v === null) {
getFillColor = [130, 109, 186];
getLineColor = [0, 0, 0, 100];
lineWidthMinPixels = 0.5;
}
if (g === 'Polygon' && v === 'Number') {
colorStyle = colorBinsStyle({
breaks: { stats, method: 'quantiles', bins: 5 },
colors: 'OrYel'
});
getFillColor = (d) => colorStyle(d.properties[propId]);
getLineColor = [0, 0, 0, 100];
lineWidthMinPixels = 0.5;
}
if (g === 'Polygon' && v === 'String') {
colorStyle = colorCategoriesStyle({
categories: { stats, top: 10 },
colors: 'Bold'
});
getFillColor = (d) => colorStyle(d.properties[propId]);
getLineColor = [0, 0, 0, 100];
lineWidthMinPixels = 0.5;
}
if (g === 'LineString' && v === null) {
getLineColor = [76, 200, 163];
lineWidthMinPixels = 2;
}
if (g === 'LineString' && v === 'Number') {
colorStyle = colorBinsStyle({
breaks: { stats, method: 'quantiles', bins: 5 },
colors: 'SunsetDark'
});
getLineColor = (d) => colorStyle(d.properties[propId]);
lineWidthMinPixels = 2;
}
if (g === 'LineString' && v === 'String') {
colorStyle = colorCategoriesStyle({
categories: { stats, top: 10 },
colors: 'Bold'
});
getLineColor = (d) => colorStyle(d.properties[propId]);
lineWidthMinPixels = 2;
}
if (g === 'Point' && v === null) {
getFillColor = [238, 77, 90];
getLineColor = [0, 0, 0, 100];
pointRadiusMinPixels = 3;
lineWidthMinPixels = 0.5;
}
if (g === 'Point' && v === 'Number') {
colorStyle = colorBinsStyle({
breaks: { stats, method: 'quantiles', bins: 5 },
colors: 'SunsetDark'
});
getFillColor = (d) => colorStyle(d.properties[propId]);
getLineColor = [0, 0, 0, 100];
pointRadiusMinPixels = 3;
lineWidthMinPixels = 0.5;
}
if (g === 'Point' && v === 'String') {
colorStyle = colorCategoriesStyle({
categories: { stats, top: 10 },
colors: 'Safe'
});
getFillColor = (d) => colorStyle(d.properties[propId]);
getLineColor = [0, 0, 0, 100];
pointRadiusMinPixels = 3;
lineWidthMinPixels = 0.5;
}
},
resetColorStyle () {
propId = undefined;
colorStyle = undefined;
getFillColor = undefined;
getLineColor = undefined;
pointRadiusMinPixels = undefined;
lineWidthMinPixels = undefined;
}
}
};

@ -0,0 +1,40 @@
import {range} from 'd3-array';
import {scaleThreshold} from 'd3-scale';
import {gePalette, NULL_COLOR} from './utils';
const N_BINS = 5;
export default function colorBinsStyle ({ breaks, colors, nulltColor = NULL_COLOR }) {
let domain;
if (Array.isArray(breaks)) {
domain = breaks;
} else {
const {stats, method, bins = N_BINS} = breaks;
if (method === 'quantiles') {
const minQuantile = parseInt(Object.keys(stats.quantiles[0]), 10);
const maxQuantile = parseInt(Object.keys(stats.quantiles[stats.quantiles.length - 1]), 10);
if (bins < minQuantile || bins > maxQuantile) {
throw new Error(
`Invalid bins value. It shoud be between ${minQuantile} and ${maxQuantile}`
);
}
const quantiles = stats.quantiles.find(d => d.hasOwnProperty(bins));
domain = quantiles[bins];
} else {
const {min, max} = stats;
const step = (max - min) / bins;
domain = range(min + step, max, step);
}
}
const palette = typeof colors === 'string' ? gePalette(colors, domain.length + 1) : colors;
const color = scaleThreshold()
.domain(domain)
.range(palette);
return d => {
return d === (undefined || null) ? nulltColor : color(d);
};
}

@ -0,0 +1,31 @@
import {gePalette, NULL_COLOR} from './utils';
const OTHERS_COLOR = [119, 119, 119];
const TOP = 10;
export default function colorCategoriesStyle ({
categories,
colors,
nulltColor = NULL_COLOR,
othersColor = OTHERS_COLOR
}) {
let categoryList;
const colorsByCategory = {};
if (Array.isArray(categories)) {
categoryList = categories;
} else {
const {stats, top = TOP} = categories;
categoryList = stats.categories.map(c => c.category).slice(0, top);
}
const palette = typeof colors === 'string' ? gePalette(colors, categoryList.length) : colors;
for (const [i, c] of categoryList.entries()) {
colorsByCategory[c] = palette[i];
}
return d => {
return d === (undefined || null) ? nulltColor : colorsByCategory[d] || othersColor;
};
}

@ -0,0 +1,80 @@
import * as cartoColors from 'cartocolor';
export const NULL_COLOR = [204, 204, 204];
export function gePalette (name, numCategories) {
const palette = cartoColors[name];
let paletteIndex = numCategories;
if (!palette) {
throw new Error(`Palette ${name} is not found.`);
}
const palettesColorVariants = Object.keys(palette)
.filter(p => p !== 'tags')
.map(Number);
const longestPaletteIndex = Math.max(...palettesColorVariants);
const smallestPaletteIndex = Math.min(...palettesColorVariants);
if (!Number.isInteger(numCategories) || numCategories > longestPaletteIndex) {
paletteIndex = longestPaletteIndex;
} else if (numCategories < smallestPaletteIndex) {
paletteIndex = smallestPaletteIndex;
}
const colors = palette[paletteIndex];
if (palette.tags && palette.tags.includes('qualitative')) {
colors.pop();
}
return colors.map(c => hexToRgb(c));
}
function hexToRgb (hex) {
// Evaluate #ABC
let result = /^#?([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})$/i.exec(hex);
if (result) {
return [
parseInt(result[1] + result[1], 16),
parseInt(result[2] + result[2], 16),
parseInt(result[3] + result[3], 16),
255
];
}
// Evaluate #ABCD
result = /^#?([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})$/i.exec(hex);
if (result) {
return [
parseInt(result[1] + result[1], 16),
parseInt(result[2] + result[2], 16),
parseInt(result[3] + result[3], 16),
parseInt(result[4] + result[4], 16)
];
}
// Evaluate #ABCDEF
result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (result) {
return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16), 255];
}
// Evaluate #ABCDEFAF
result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (result) {
return [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16),
parseInt(result[4], 16)
];
}
throw new Error(`Error parsing hexadecimal color: ${hex}`);
}

@ -132,6 +132,10 @@ export default {
this.$store.dispatch('catalog/fetchDataset', {
id: this.$route.params.entity_id,
type: this.$route.params.entity_type
}),
this.$store.dispatch('catalog/fetchVariables', {
id: this.$route.params.entity_id,
type: this.$route.params.entity_type
})
]).then(() => {
this.loading = false;
@ -139,10 +143,6 @@ export default {
if (this.$route.params.entity_id !== this.dataset.slug) {
this.$router.replace({ params: { entity_id: this.dataset.slug } });
}
this.$store.dispatch('catalog/fetchVariables', {
id: this.dataset.slug,
type: this.$route.params.entity_type
});
} else {
this.$router.replace({ name: 'spatial-data-catalog' });
}

@ -38,7 +38,7 @@ export default {
return this.dataset.name;
},
defaultSource () {
return this.dataset.sample_info && this.dataset.sample_info.default_source || 'Test';
return this.dataset.sample_info && this.dataset.sample_info.default_source;
}
},
methods: {
@ -50,7 +50,7 @@ export default {
@import 'new-dashboard/styles/variables';
.catalog-dataset-map {
margin: 12px;
margin: 12px 12px 24px;
padding: 12px;
border-radius: 4px;
background: $color-primary--soft;
@ -74,7 +74,7 @@ export default {
.footer {
display: flex;
justify-content: flex-end;
margin: 24px 12px;
margin: 0 12px 24px 12px;
span {
display: flex;

46
package-lock.json generated

@ -1548,6 +1548,11 @@
"underscore": "1.8.3"
}
},
"d3-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
"integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
},
"perfect-scrollbar": {
"version": "git://github.com/CartoDB/perfect-scrollbar.git#f2b66c76ad3718d3c704bd7e1693ea382e44e64d",
"from": "git://github.com/CartoDB/perfect-scrollbar.git#master"
@ -8128,9 +8133,9 @@
"dev": true
},
"cartocolor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cartocolor/-/cartocolor-4.0.0.tgz",
"integrity": "sha1-hBoyIti1sicY2dVFseW5cssm6zY=",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/cartocolor/-/cartocolor-4.0.2.tgz",
"integrity": "sha512-+Gh9mb6lFxsDOLQlBLPxAHCnWXlg2W8q3AcVwqRcy95TdBbcOU89Wrb6h2Hd/6Ww1Kc1pzXmUdpnWD+xeCG0dg==",
"requires": {
"colorbrewer": "1.0.0"
}
@ -10567,9 +10572,9 @@
"integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g="
},
"d3-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
"integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.8.0.tgz",
"integrity": "sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw=="
},
"d3-color": {
"version": "1.2.3",
@ -10594,6 +10599,18 @@
"resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz",
"integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg="
},
"d3-scale": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
"integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
"requires": {
"d3-array": "^2.3.0",
"d3-format": "1 - 2",
"d3-interpolate": "1.2.0 - 2",
"d3-time": "1 - 2",
"d3-time-format": "2 - 3"
}
},
"d3-time": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
@ -15119,6 +15136,13 @@
"torque.js": "github:CartoDB/torque#master",
"underscore": "1.8.3",
"whatwg-fetch": "^2.0.3"
},
"dependencies": {
"d3-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
"integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
}
}
},
"internal-ip": {
@ -27668,6 +27692,16 @@
"es6-promise": "3.1.2",
"postcss": "5.0.19",
"postcss-value-parser": "3.3.0"
},
"dependencies": {
"cartocolor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cartocolor/-/cartocolor-4.0.0.tgz",
"integrity": "sha1-hBoyIti1sicY2dVFseW5cssm6zY=",
"requires": {
"colorbrewer": "1.0.0"
}
}
}
},
"turf-jenks": {

@ -38,14 +38,16 @@
"browserify-shim": "3.8.12",
"camshaft-reference": "0.34.0",
"carto": "cartodb/carto#master",
"cartocolor": "4.0.0",
"cartocolor": "^4.0.2",
"cartodb-pecan": "0.2.x",
"clipboard": "1.6.1",
"codemirror": "5.14.2",
"confetti-js": "0.0.14",
"core-js": "^3.6.5",
"d3-array": "^2.8.0",
"d3-interpolate": "^1.1.6",
"d3-queue": "^3.0.7",
"d3-scale": "^3.2.3",
"date-fns": "^1.29.0",
"fastclick": "^1.0.6",
"html-webpack-plugin": "^3.2.0",

Loading…
Cancel
Save