v1.0版本主要内容包含:图形绘制,渲染,图形生成,虚拟上传,路径裁剪,地图填色,数据填色,数据处理等。
v2.0进阶版本更新:(主要技术栈:D3.js)。
v2.0最新更新时间:2023-06-21。
v2.0主要更新内容:水系、湖泊、内外边界分离等。
进阶:1)、封装公共部分
import * as d3 from 'd3';
import * as turf from '@turf/turf'
import { HttpService } from 'src/app/shared/services/http/http.service';
import PubSub from 'pubsub-js'
export class D3Drawer {
private pathGenerator: any;
private projection: any;
private element: any;
// private border: any;
private coordinate: any = null;
private httpService: HttpService;
private colorChart: any = null;
private width: any = null;
private height: any = null;
private zoomContains: any = [];
private contains: any = [];
// private changeClickElement: any = null;
private currentTag: any = null;
private zoom: any = null;
private currentTags: Map<string, any> = new Map<string, any>();
/**
*
* @param httpService
* @param width 图片宽度
* @param height 图片高度
* @param id svg dom id
*/
public constructor(httpService: HttpService, width: number, height: number, id: string) {
this.httpService = httpService;
this.width = width;
this.height = height;
this.element = d3.select(id)
.attr("width", this.width)
.attr("height", this.height);
// const { event } = require("d3-selection");//动态引入event
// event.preventDefault();
}
public export(exportName: any = 'name') {
var serializer = new XMLSerializer();
var source = '<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(this.element.node());
var image = new Image;
image.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source);
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
var context: any = canvas.getContext("2d");
context.fillStyle = '#fff';
context.fillRect(0, 0, this.width, this.height);
image.onload = function () {
context.drawImage(image, 0, 0);
var a = document.createElement("a");
a.download = exportName + ".png";
a.href = canvas.toDataURL("image/png");
a.click();
};
}
/**
* 虚拟上传
* @param pagesType 所属页面
* @time ***
* @updataTime 2023-6-16
* @writer wy
*/
public virtualUpload(pagesType: string) {
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
var context: any = canvas.getContext("2d");
context.fillStyle = '#fff';
context.fillRect(0, 0, this.width, this.height);
var serializer = new XMLSerializer(); // 文档序列化
var source = '<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(this.element.node()); // 将整个文档序列化为包含XML的字符串
const imgUrl = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source)
var image = new Image();
image.onload = function () {
context.drawImage(image, 0, 0);
image.onload = null;
}
image.src = imgUrl;
setTimeout(() => {
var basePath = canvas.toDataURL("image/png")
PubSub.publish('Base64' + pagesType, basePath);
}, 500)
}
async exportRadarPicture(exportName: any = 'name', radarPictureUrl: string = '') { // 导出雷达图片
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
var context: any = canvas.getContext("2d");
context.fillStyle = '#fff';
context.fillRect(0, 0, this.width, this.height);
await this.createBaseMapImage(context);
var image = new Image;
image.setAttribute('crossOrigin', 'anonymous');
image.src = radarPictureUrl
image.onload = function () {
context.drawImage(image, 0, 0, canvas.width, canvas.height);
var a = document.createElement("a");
a.download = exportName + ".png";
a.href = canvas.toDataURL("image/png");
a.click();
};
}
public createBaseMapImage(context) {
var serializer = new XMLSerializer();
var image = new Image;
var source = '<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(this.element.node());
image.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source);
image.onload = function () {
context.drawImage(image, 0, 0);
};
}
/**
*
* @param startX 地图开始 水平位置
* @param startY 地图开始 垂直位置
* @param width 地图宽度
* @param height 地图高度
*/
public initMapInfo(startX: number, startY: number, width: number, height: number, border: any) {
this.projection = d3.geoMercator().fitExtent( // 墨卡托坐标系以及坐标映射
[
[startX, startY],
[width, height]
], border);
this.pathGenerator = d3.geoPath()
.projection(this.projection);
this.zoom = d3.zoom() //设置监听范围
.on('start', () => {
this.element.style('cursor', 'pointer')
})
.on('end', () => {
this.element.style('cursor', 'default')
})
.on("zoom", (d: any, i: any) => {
const t = d.transform;
this.currentTags.set(this.currentTag, t);
this.zoomContains.forEach((target: any) => {
target.attr("transform", `translate(${t.x}, ${t.y}) scale(${t.k})`); //改变mapContainer的位置及缩放
});
});
this.element.call(this.zoom)
.on('dblclick.zoom', null);
}
public setCoordinate(border: any) {
// const response: any = await this.httpService.getSync(borderUrl, "");
// this.border = response;
this.coordinate = Coordinate.createByBbox(border);
}
public async interpolate(values: Array<Station>, interpolateUrl: string, colorChart: ColorChart, clipId: string, tag: string) {
this.colorChart = colorChart;
let response: any = await this.httpService.postSync(interpolateUrl, this.toInterpolateValues(values));
if (response.state != 0) {
throw new Error(response.message);
}
const isobandFeatures = this.isobands(response.data);
this.drawContour(isobandFeatures, clipId, tag);
const legendElement = this.colorChart.drawLegend(this.element, this.height, this.contains, 'color');
this.bindMouseEvent(legendElement, 'color');
}
/**
* 差值/色斑图填充
* @param values
* @param elementCode
* @param interpolateUrl
* @param clipId
* @param tag
* @time 2023-05-05
* @updataTime 2023-6-16
* @writer wy
*/
public async interpolatedColorValue(values: Array<Station>, elementCode: string, interpolateUrl: string, clipId: string, tag: string, border: any = null, isAnomaly: boolean = false) {
// this.colorChart = colorChart;
let response: any = await this.httpService.postSync(interpolateUrl, this.toInterpolatedColorValues(values, elementCode));
if (response.state != 0) {
throw new Error(response.message);
}
let colorArry: any = [];
let colorValue: any = [];
let colorLeng: any = null;
let colorScale: any = response.data.colorScale
for (let i = 0; i < colorScale.length; i++) {
colorArry.push(colorScale[i].color)
colorValue.push(colorScale[i].start, colorScale[i].end)
}
colorArry.reverse()
colorValue = colorValue.filter((value, index, array) => { return array.indexOf(value) == index }).sort((a, b) => b - a)
colorLeng = response.data.colorScale[0].units
this.colorChart = new ColorChart(colorArry, colorValue, colorLeng, true, true);
const isobandFeatures = this.isobands(response.data.data);
if (border == null) this.drawContour(isobandFeatures, clipId, tag);
else this.drawContourClipBorder(isobandFeatures, border, clipId, tag, isAnomaly);
const legendElement = this.colorChart.drawLegend(this.element, this.height, this.contains, 'color');
this.bindMouseEvent(legendElement, 'color');
}
/**
* 产品制作
* @param border
* @param river
* @param values
* @param elementCode
* @param interpolateUrl
* @param colorChart
* @param clipId
* @param tag
* @param isAnomaly 距平判断
* @time ***
* @updataTime 2023-6-16
* @writer wy
*/
public async interpolateColorValueRain(border: any, values: Array<Station>, elementCode: string, interpolateUrl: string, colorChart: ColorChart, clipId: string, tag: string, isAnomaly: boolean = false) {
this.colorChart = colorChart;
let response: any = await this.httpService.postSync(interpolateUrl, this.toInterpolatedColorValues(values, elementCode));
if (response.state != 0) {
throw new Error(response.message);
}
const isobandFeatures = this.isobands(response.data.data);
// this.drawContour(isobandFeatures, clipId, tag);
this.drawContourClipBorder(isobandFeatures, border, clipId, tag, isAnomaly);
const legendElement = this.colorChart.drawLegend(this.element, this.height, this.contains, 'color');
this.bindMouseEvent(legendElement, 'color');
}
/**
* 自定义地区图例生成
* @param x 图例水平位置
* @param y 图例垂直位置
* @param colorValues 图例内容
* @param tag 移动属性
* @param legendStyle 图例样式(分体式:splitType,分体单位式:splitUnitType,垂直式:verticalType,垂直单位式:verticalUnitType)
* @time ***
* @updataTime 2023-6-16
* @writer wy
*/
public async interReginColorLegendMethod(x: number, y: number, colorValues: Array<any>, tag: string = 'color', legendStyle: string = 'splitType', unitType: string = 'mm') { // 地区图例 直接生成
const legendElement = await this.drawReginColorLegend(x, y, colorValues, tag, legendStyle, unitType);
this.bindMouseEvent(legendElement, tag);
}
/**
* 绘制图例
* @param x 图例水平位置
* @param y 图例垂直位置
* @param colorValues 颜色值集
* @param tag 移动属性
* @param legendStyle 图例样式
* @param unitType 单位样式
* @returns 生成好的图例
* @time ***
* @updataTime 2023-6-16
* @writer wy
*/
public drawReginColorLegend(x: number, y: number, colorValues: Array<any>, tag: string , legendStyle: string, unitType: string): any {
const reginLegendWidth = (this.height / (legendStyle == 'splitType' || legendStyle == 'splitUnitType' ? 4 : 3)) / colorValues.length;
const legendHeight = this.height - 10 - reginLegendWidth;
const legendStyleJudeg: boolean = legendStyle == 'splitType' || legendStyle == 'splitUnitType' ? true : false;
const legendUnitStyleJudeg: boolean = legendStyle !== 'splitType' && legendStyle !== 'verticalType' ? true : false;
let legendElement = this.element.append('g')
.attr('class', 'legend')
.selectAll('g')
.data(colorValues)
.enter().append("g");
//绘制文字后方的颜色框或线
legendElement.append("rect")
.attr("x", 5 + x)
.attr("y", (d: any, i: number) => {
return legendStyleJudeg ? legendHeight - (i * reginLegendWidth) - y : legendHeight - (i * reginLegendWidth) - (y / 1.2);
})
.attr("height", legendStyleJudeg ? 20 : reginLegendWidth)
.attr("width", legendStyleJudeg ? reginLegendWidth : 20)
.style('stroke-width', 1)
.style('stroke', '#3e3b3bd9')
.style("fill", (d: any) => {
return d.color
});
//绘制图例文字
legendElement.append("text")
.attr("x", legendStyleJudeg ? reginLegendWidth + 10 + x : reginLegendWidth + x)
.attr("y", (d: any, i: number) => {
return legendStyleJudeg ? legendHeight + 15 - (i * reginLegendWidth) - y : legendHeight + reginLegendWidth + 5 - (i * reginLegendWidth) - (y / 1.2);
})
.style("text-anchor", "start") //样式对齐
.text((d: any) => {
return d.value;
});
// let unitElement = this.element.append("g")
// .attr("class", "color-unit")
// .append('text')
// .attr("x", 5 + x)
// .attr("y", legendStyleJudeg ? legendHeight + 30 - reginLegendWidth*colorValues.length - y : legendHeight + reginLegendWidth - 5 - (colorValues.length * reginLegendWidth) - (y / 1.2))
// .style('font-weight', 500)
// .style('font-family', 'Arial')
// .style('fill', 'black')
// .text('单位:' + unitType);
// // .text('单位:' + this.unit);
legendElement.tag = tag;
// if(legendUnitStyleJudeg) unitElement.t