fix(layer): 数据映射设置成不存在字段时,不能正常渲染

This commit is contained in:
thinkinggis 2020-05-12 14:24:09 +08:00
parent b6a92b1994
commit 0233855ab4
12 changed files with 383 additions and 59 deletions

View File

@ -131,6 +131,7 @@ export interface IStyleAttributeInitializationOptions {
scale?: {
field: StyleAttributeField;
values: unknown[] | string;
defaultValues: unknown[] | string;
names: string[] | number[];
type: StyleScaleType;
callback?: (...args: any[]) => [];

View File

@ -23,6 +23,7 @@ export default class StyleAttribute implements IStyleAttribute {
names: string[];
field: string | string[];
values: unknown[];
defaultValues: unknown[];
callback?: (...args: any[]) => [];
scalers?: IAttributeScale[];
};
@ -76,7 +77,7 @@ export default class StyleAttribute implements IStyleAttribute {
private defaultCallback = (params: unknown[]): unknown[] => {
// 没有 params 的情况,是指没有指定 fields直接返回配置的 values 常量
if (params.length === 0) {
return this.scale?.values || [];
return this.scale?.defaultValues || [];
}
return params.map((param, idx) => {
const scaleFunc = this.scale?.scalers![idx].func;

View File

@ -29,6 +29,7 @@ export default class BaseLayer extends EventEmitter {
protected scene: Scene;
protected options: IDistrictLayerOption;
protected layers: ILayer[] = [];
protected fillData: any;
private popup: IPopup;
constructor(scene: Scene, option: Partial<IDistrictLayerOption> = {}) {
@ -48,6 +49,34 @@ export default class BaseLayer extends EventEmitter {
public hide() {
this.layers.forEach((layer) => layer.hide());
}
public setOption(newOption: {[key: string]: any }) {
this.options = merge(this.options, newOption);
}
public updateData(
newData: Array<{ [key: string]: any }>,
joinByField?: [string, string],
) {
this.setOption({
data: newData,
joinBy: joinByField,
});
const { data = [], joinBy } = this.options;
this.fillLayer.setData(this.fillData, {
transforms:
data.length === 0
? []
: [
{
type: 'join',
sourceField: joinBy[1], // data1 对应字段名
targetField: joinBy[0], // data 对应字段名 绑定到的地理数据
data,
},
],
});
}
protected async fetchData(data: { url: string; type: string }) {
if (data.type === 'pbf') {
const buffer = await (await fetch(data.url)).arrayBuffer();
@ -62,6 +91,7 @@ export default class BaseLayer extends EventEmitter {
zIndex: 0,
depth: 1,
adcode: [],
joinBy: ['name', 'name'],
label: {
enable: true,
color: '#000',
@ -76,16 +106,20 @@ export default class BaseLayer extends EventEmitter {
scale: null,
field: null,
values: '#fff',
style: {
opacity: 1.0,
},
activeColor: 'rgba(0,0,255,0.3)',
},
autoFit: true,
stroke: '#d95f0e',
stroke: '#bdbdbd',
strokeWidth: 0.6,
cityStroke: 'rgba(255,255,255,0.6)',
cityStroke: '#636363',
cityStrokeWidth: 0.6,
countyStrokeWidth: 0.6,
provinceStrokeWidth: 0.6,
provinceStroke: '#fff',
countyStroke: 'rgba(255,255,255,0.6)',
provinceStroke: '#f0f0f0',
countyStroke: '#525252',
coastlineStroke: '#4190da',
coastlineWidth: 1,
nationalStroke: '#c994c7',
@ -104,7 +138,8 @@ export default class BaseLayer extends EventEmitter {
protected addFillLayer(fillCountry: any) {
// 添加省份填充
const { popup, data = [], fill, autoFit } = this.options;
const { popup, data = [], fill, autoFit, joinBy } = this.options;
this.fillData = fillCountry;
const fillLayer = new PolygonLayer({
autoFit,
}).source(fillCountry, {
@ -114,8 +149,8 @@ export default class BaseLayer extends EventEmitter {
: [
{
type: 'join',
sourceField: 'name', // data1 对应字段名
targetField: 'name', // data 对应字段名 绑定到的地理数据
sourceField: joinBy[1], // data1 对应字段名
targetField: joinBy[0], // data 对应字段名 绑定到的地理数据
data,
},
],
@ -133,11 +168,9 @@ export default class BaseLayer extends EventEmitter {
fillLayer
.shape('fill')
.active({
color: 'rgba(0,0,255,0.3)',
color: fill.activeColor as string,
})
.style({
opacity: 1,
});
.style(fill.style);
this.fillLayer = fillLayer;
this.layers.push(fillLayer);
this.scene.addLayer(fillLayer);

View File

@ -16,6 +16,12 @@ export default class CountryLayer extends BaseLayer {
const { depth } = this.options;
this.loadData().then(([fillData, fillLabel]) => {
this.addFillLayer(fillData);
// const labeldata = fillData.features.map((feature: any) => {
// return {
// ...feature.properties,
// center: [feature.properties.x, feature.properties.y],
// };
// });
if (fillLabel && this.options.label?.enable) {
this.addLabelLayer(
fillLabel.filter((v: any) => {
@ -33,7 +39,7 @@ export default class CountryLayer extends BaseLayer {
this.addCityBorder(countryConfig.cityLine);
}
if (depth === 3 * 1) {
this.addCountryBorder(countryConfig.countryLine);
this.addCountyBorder(countryConfig.countryLine);
}
}
// 国界,省界
@ -92,6 +98,8 @@ export default class CountryLayer extends BaseLayer {
private async addNationBorder(boundaries: any, boundaries2: any) {
const {
nationalStroke,
provinceStroke,
provinceStrokeWidth,
nationalWidth,
chinaNationalStroke,
chinaNationalWidth,
@ -108,7 +116,7 @@ export default class CountryLayer extends BaseLayer {
.source(boundaries)
.size('type', (v: string) => {
if (v === '3') {
return strokeWidth;
return provinceStrokeWidth;
} else if (v === '2') {
return coastlineWidth;
} else if (v === '0') {
@ -120,7 +128,7 @@ export default class CountryLayer extends BaseLayer {
.shape('line')
.color('type', (v: string) => {
if (v === '3') {
return stroke;
return provinceStroke;
} else if (v === '2') {
return coastlineStroke;
} else if (v === '0') {
@ -146,7 +154,7 @@ export default class CountryLayer extends BaseLayer {
this.scene.addLayer(lineLayer2);
this.layers.push(lineLayer, lineLayer2);
}
// 省级边界
// 边界
private async addCityBorder(cfg: any) {
const border1 = await this.fetchData(cfg);
const { cityStroke, cityStrokeWidth } = this.options;
@ -164,7 +172,7 @@ export default class CountryLayer extends BaseLayer {
}
// 县级边界
private async addCountryBorder(cfg: any) {
private async addCountyBorder(cfg: any) {
const border1 = await this.fetchData(cfg);
const { countyStrokeWidth, countyStroke } = this.options;
const cityline = new LineLayer({
@ -196,7 +204,7 @@ export default class CountryLayer extends BaseLayer {
this.scene.addLayer(labelLayer);
this.scene.addLayer(labelLayer1);
this.scene.addLayer(labelLayer2);
this.layers.push(labelLayer, labelLayer1);
this.layers.push(labelLayer, labelLayer1, labelLayer2);
}
private addText(labelData: any, option: any, offset: [number, number]) {

View File

@ -1,7 +1,7 @@
import { Scene } from '@antv/l7';
import CityLayer from './city';
import CountryLayer from './country';
import { IDistrictLayerOption } from './interface';
import { IDistrictLayerOption, adcodeType } from './interface';
import ProvinceLayer from './province';
export default class DrillDownLayer {
@ -9,6 +9,7 @@ export default class DrillDownLayer {
private cityLayer: CityLayer;
private countryLayer: CountryLayer;
private scene: Scene;
private drillState: 'province' | 'city' | 'county' = 'province';
constructor(scene: Scene, option: Partial<IDistrictLayerOption>) {
const cfg = this.getDefaultOption();
this.scene = scene;
@ -42,14 +43,13 @@ export default class DrillDownLayer {
this.countryLayer.hide();
// 更新市级行政区划
this.provinceLayer.updateDistrict([e.feature.properties.adcode]);
this.drillState = 'city';
});
}
public addProvinceEvent() {
this.provinceLayer.fillLayer.on('undblclick', () => {
this.countryLayer.show();
this.countryLayer.fillLayer.fitBounds();
this.provinceLayer.hide();
this.drillUp();
});
this.provinceLayer.fillLayer.on('click', (e: any) => {
this.provinceLayer.hide();
@ -59,14 +59,13 @@ export default class DrillDownLayer {
}
// 更新县级行政区划
this.cityLayer.updateDistrict([adcode]);
this.drillState = 'county';
});
}
public addCityEvent() {
this.cityLayer.fillLayer.on('undblclick', () => {
this.provinceLayer.show();
this.provinceLayer.fillLayer.fitBounds();
this.cityLayer.hide();
this.drillUp();
});
}
@ -75,4 +74,47 @@ export default class DrillDownLayer {
this.provinceLayer.destroy();
this.cityLayer.destroy();
}
public showProvinceView() {
this.provinceLayer.show();
this.provinceLayer.fillLayer.fitBounds();
this.cityLayer.hide();
this.drillState = 'city';
}
public showCityView() {
this.countryLayer.show();
this.countryLayer.fillLayer.fitBounds();
this.provinceLayer.hide();
this.drillState = 'county';
}
/**
*
*/
public drillUp() {
switch (this.drillState) {
case 'county':
this.provinceLayer.show();
this.provinceLayer.fillLayer.fitBounds();
this.cityLayer.hide();
this.drillState = 'city';
break;
case 'city':
this.countryLayer.show();
this.countryLayer.fillLayer.fitBounds();
this.provinceLayer.hide();
this.drillState = 'province';
break;
}
}
public DrillDown(adcode: adcodeType, data: any) {
switch (this.drillState) {
case 'province':
this.showProvinceView();
break;
case 'city':
this.showCityView();
break;
}
}
}

View File

@ -13,6 +13,7 @@ export type adcodeType = string[] | string | number | number[];
export interface IDistrictLayerOption {
zIndex: number;
data?: Array<{ [key: string]: any }>;
joinBy: [string, string];
adcode: adcodeType;
depth: 0 | 1 | 2 | 3;
label: Partial<ILabelOption>;
@ -20,6 +21,8 @@ export interface IDistrictLayerOption {
scale: ScaleTypeName | null;
field: string | null;
values: StyleAttributeOption;
style: any;
activeColor: string;
}>;
autoFit: boolean;
stroke: string;

View File

@ -16,35 +16,35 @@ export interface IProvinceLayerOption extends IDistrictLayerOption {
adcode: adcodeType;
}
export default class ProvinceLayer extends BaseLayer {
private fillData: any;
private lineData: any;
private labelData: any;
private fillRawData: any;
private lineRawData: any;
private labelRawData: any;
constructor(scene: Scene, option: Partial<IProvinceLayerOption> = {}) {
super(scene, option);
this.addProvinceFillLayer();
this.addProvinceLineLayer();
}
// 通过adcode 更新
public updateDistrict(adcode: adcodeType) {
public updateDistrict(
adcode: adcodeType,
newData: Array<{ [key: string]: any }> = [],
joinByField?: [string, string],
) {
if (!adcode && Array.isArray(adcode) && adcode.length === 0) {
this.hide();
return;
}
const fillData = this.filterData(this.fillData, adcode);
const lineData = this.filterData(this.lineData, adcode);
const labelData = this.filterLabelData(this.labelData, adcode);
this.fillLayer.setData(fillData);
this.setOption({ adcode });
const fillData = this.filterData(this.fillRawData, adcode);
const lineData = this.filterData(this.lineRawData, adcode);
const labelData = this.filterLabelData(this.labelRawData, adcode);
this.fillData = fillData;
this.updateData(newData, joinByField);
this.lineLayer.setData(lineData);
this.labelLayer.setData(labelData);
this.show();
}
// 更新渲染数据
public updateData() {
return 'update data';
}
protected getDefaultOption(): IProvinceLayerOption {
const config = super.getDefaultOption();
return merge({}, config, {
@ -102,15 +102,15 @@ export default class ProvinceLayer extends BaseLayer {
const countryConfig = DataConfig.country.CHN[depth];
const fillData = await this.fetchData(countryConfig.fill);
this.labelData = fillData.features.map((feature: any) => {
this.labelRawData = fillData.features.map((feature: any) => {
return {
...feature.properties,
center: [feature.properties.x, feature.properties.y],
};
});
const data = this.filterData(fillData, adcode);
const labelData = this.filterLabelData(this.labelData, adcode);
this.fillData = fillData;
const labelData = this.filterLabelData(this.labelRawData, adcode);
this.fillRawData = fillData;
this.addFillLayer(data);
this.addLabelLayer(labelData);
}
@ -120,7 +120,7 @@ export default class ProvinceLayer extends BaseLayer {
const countryConfig = DataConfig.country.CHN[depth];
const fillData = await this.fetchData(countryConfig.line);
const data = this.filterData(fillData, adcode);
this.lineData = fillData;
this.lineRawData = fillData;
this.addFillLine(data);
}
}

View File

@ -133,7 +133,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
scale.scale.domain(tick);
}
}
scale.scale.range(attributeScale.values);
scale.scale.range(attributeScale.values); // 判断常量, 默认值
} else if (scale.option?.type === 'cat') {
// 如果没有设置初值且 类型为catrange ==domain;
@ -144,7 +144,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
} else {
// 设置attribute 常量值 常量直接在value取值
attributeScale.type = StyleScaleType.CONSTANT;
attributeScale.values = scales.map((scale, index) => {
attributeScale.defaultValues = scales.map((scale, index) => {
return scale.scale(attributeScale.names[index]);
});
}

View File

@ -24,6 +24,5 @@ export function join(geoData: IParserData, options: IJoinOption) {
...item,
};
});
return geoData;
}

View File

@ -23,12 +23,189 @@ export default class Country extends React.Component {
maxZoom: 10,
}),
});
const ProvinceData = [
{
name: '云南省',
code: 530000,
value: 17881.12,
},
{
name: '黑龙江省',
code: 230000,
value: 16361.62,
},
{
name: '贵州省',
code: 520000,
value: 14806.45,
},
{
name: '北京市',
code: 110000,
value: 30319.98,
},
{
name: '河北省',
code: 130000,
value: 36010.27,
},
{
name: '山西省',
code: 140000,
value: 16818.11,
},
{
name: '吉林省',
code: 220000,
value: 15074,
},
{
name: '宁夏回族自治区',
code: 640000,
value: 3705.18,
},
{
name: '辽宁省',
code: 210000,
value: 25315.35,
},
{
name: '海南省',
code: 460000,
value: 4832.05,
},
{
name: '内蒙古自治区',
code: 150000,
value: 17289.22,
},
{
name: '天津市',
code: 120000,
value: 18809.64,
},
{
name: '新疆维吾尔自治区',
code: 650000,
value: 12199.08,
},
{
name: '上海市',
code: 310000,
value: 32679.87,
},
{
name: '陕西省',
code: 610000,
value: 24438.32,
},
{
name: '甘肃省',
code: 620000,
value: 8246.07,
},
{
name: '安徽省',
code: 340000,
value: 30006.82,
},
{
name: '香港特别行政区',
code: 810000,
value: 0,
},
{
name: '广东省',
code: 440000,
value: 97277.77,
},
{
name: '河南省',
code: 410000,
value: 48055.86,
},
{
name: '湖南省',
code: 430000,
value: 36425.78,
},
{
name: '江西省',
code: 360000,
value: 21984.78,
},
{
name: '四川省',
code: 510000,
value: 40678.13,
},
{
name: '广西壮族自治区',
code: 450000,
value: 20353.51,
},
{
name: '江苏省',
code: 320000,
value: 92595.4,
},
{
name: '澳门特别行政区',
code: 820000,
value: null,
},
{
name: '浙江省',
code: 330000,
value: 56197.15,
},
{
name: '山东省',
code: 370000,
value: 76469.67,
},
{
name: '青海省',
code: 630000,
value: 2865.23,
},
{
name: '重庆市',
code: 500000,
value: 20363.19,
},
{
name: '福建省',
code: 350000,
value: 35804.04,
},
{
name: '湖北省',
code: 420000,
value: 39366.55,
},
{
name: '西藏自治区',
code: 540000,
value: 1477.63,
},
{
name: '台湾省',
code: 710000,
value: null,
},
];
scene.on('loaded', () => {
const Layer = new CountryLayer(scene, {
data: [],
data: ProvinceData,
joinBy: ['NAME_CHN', 'name'],
// label: {
// field: 'NAME_CHN',
// textAllowOverlap: true,
// },
depth: 1,
fill: {
field: 'NAME_CHN',
field: 'value',
values: [
'#feedde',
'#fdd0a2',
@ -41,7 +218,7 @@ export default class Country extends React.Component {
popup: {
enable: true,
Html: (props) => {
return `<span>${props.NAME_CHN}</span>`;
return `<span>${props.NAME_CHN}:</span><span>${props.value}</span>`;
},
},
});

View File

@ -24,6 +24,17 @@ export default class Country extends React.Component {
this.setState({
options,
});
const response = await fetch(
'https://gw.alipayobjects.com/os/bmw-prod/149b599d-21ef-4c24-812c-20deaee90e20.json',
);
const provinceData = await response.json();
const data = Object.keys(provinceData).map((key: string) => {
return {
code: parseInt(key),
name: provinceData[key][0],
pop: provinceData[key][2] * 1,
};
});
const scene = new Scene({
id: 'map',
map: new Mapbox({
@ -37,15 +48,16 @@ export default class Country extends React.Component {
});
scene.on('loaded', () => {
this.cityLayer = new CityLayer(scene, {
data: [],
adcode: ['110100'],
data,
joinBy: ['adcode', 'code'],
adcode: ['330000', '330100'],
depth: 3,
label: {
field: 'NAME_CHN',
textAllowOverlap: false,
},
fill: {
field: 'NAME_CHN',
field: 'pop',
values: [
'#feedde',
'#fdd0a2',
@ -58,7 +70,7 @@ export default class Country extends React.Component {
popup: {
enable: true,
Html: (props) => {
return `<span>${props.NAME_CHN}</span>`;
return `<span>${props.NAME_CHN}:</span><span>${props.pop}</span>`;
},
},
});
@ -78,7 +90,7 @@ export default class Country extends React.Component {
top: '10px',
}}
options={this.state.options}
defaultValue={['110000', '110100']}
defaultValue={['330000', '330100']}
onChange={this.handleProvinceChange}
placeholder="Please select"
/>
@ -97,5 +109,6 @@ export default class Country extends React.Component {
}
private handleProvinceChange = (value: string[]) => {
this.cityLayer.updateDistrict([value[1]]);
console.log(this.cityLayer.fillLayer);
};
}

View File

@ -5,142 +5,177 @@ import { GaodeMap, Mapbox } from '@antv/l7-maps';
import { Select } from 'antd';
import * as React from 'react';
const { Option } = Select;
const ProvinceData = [
{
NAME_CHN: '云南省',
adcode: 530000,
value: 17881.12,
},
{
NAME_CHN: '黑龙江省',
adcode: 230000,
value: 16361.62,
},
{
NAME_CHN: '贵州省',
adcode: 520000,
value: 14806.45,
},
{
NAME_CHN: '北京市',
adcode: 110000,
value: 30319.98,
},
{
NAME_CHN: '河北省',
adcode: 130000,
value: 36010.27,
},
{
NAME_CHN: '山西省',
adcode: 140000,
value: 16818.11,
},
{
NAME_CHN: '吉林省',
adcode: 220000,
value: 15074,
},
{
NAME_CHN: '宁夏回族自治区',
adcode: 640000,
value: 3705.18,
},
{
NAME_CHN: '辽宁省',
adcode: 210000,
value: 25315.35,
},
{
NAME_CHN: '海南省',
adcode: 460000,
value: 4832.05,
},
{
NAME_CHN: '内蒙古自治区',
adcode: 150000,
value: 17289.22,
},
{
NAME_CHN: '天津市',
adcode: 120000,
value: 18809.64,
},
{
NAME_CHN: '新疆维吾尔自治区',
adcode: 650000,
value: 12199.08,
},
{
NAME_CHN: '上海市',
adcode: 310000,
value: 32679.87,
},
{
NAME_CHN: '陕西省',
adcode: 610000,
value: 24438.32,
},
{
NAME_CHN: '甘肃省',
adcode: 620000,
value: 8246.07,
},
{
NAME_CHN: '安徽省',
adcode: 340000,
value: 30006.82,
},
{
NAME_CHN: '香港特别行政区',
adcode: 810000,
value: 0,
},
{
NAME_CHN: '广东省',
adcode: 440000,
value: 97277.77,
},
{
NAME_CHN: '河南省',
adcode: 410000,
value: 48055.86,
},
{
NAME_CHN: '湖南省',
adcode: 430000,
value: 36425.78,
},
{
NAME_CHN: '江西省',
adcode: 360000,
value: 21984.78,
},
{
NAME_CHN: '四川省',
adcode: 510000,
value: 40678.13,
},
{
NAME_CHN: '广西壮族自治区',
adcode: 450000,
value: 20353.51,
},
{
NAME_CHN: '江苏省',
adcode: 320000,
value: 92595.4,
},
{
NAME_CHN: '澳门特别行政区',
adcode: 820000,
value: null,
},
{
NAME_CHN: '浙江省',
adcode: 330000,
value: 56197.15,
},
{
NAME_CHN: '山东省',
adcode: 370000,
value: 76469.67,
},
{
NAME_CHN: '青海省',
adcode: 630000,
value: 2865.23,
},
{
NAME_CHN: '重庆市',
adcode: 500000,
value: 20363.19,
},
{
NAME_CHN: '福建省',
adcode: 350000,
value: 35804.04,
},
{
NAME_CHN: '湖北省',
adcode: 420000,
value: 39366.55,
},
{
NAME_CHN: '西藏自治区',
adcode: 540000,
value: 1477.63,
},
{
NAME_CHN: '台湾省',
adcode: 710000,
value: null,
},
];
export default class Country extends React.Component {
@ -155,6 +190,17 @@ export default class Country extends React.Component {
}
public async componentDidMount() {
const response = await fetch(
'https://gw.alipayobjects.com/os/bmw-prod/149b599d-21ef-4c24-812c-20deaee90e20.json',
);
const provinceData = await response.json();
const data = Object.keys(provinceData).map((key: string) => {
return {
code: key,
name: provinceData[key][0],
pop: provinceData[key][3],
};
});
const scene = new Scene({
id: 'map',
map: new Mapbox({
@ -170,15 +216,16 @@ export default class Country extends React.Component {
scene.on('loaded', () => {
const { province } = this.state;
this.provinceLayer = new ProvinceLayer(scene, {
data: [],
adcode: [],
data,
joinBy: ['adcode', 'code'],
adcode: ['330000'],
depth: 3,
label: {
field: 'NAME_CHN',
textAllowOverlap: false,
},
fill: {
field: 'NAME_CHN',
field: 'pop',
values: [
'#feedde',
'#fdd0a2',
@ -191,7 +238,7 @@ export default class Country extends React.Component {
popup: {
enable: true,
Html: (props) => {
return `<span>${props.NAME_CHN}</span>`;
return `<span>${props.NAME_CHN}:</span><span>${props.pop}</span>`;
},
},
});