336 lines
11 KiB
JavaScript
336 lines
11 KiB
JavaScript
//taitan公司自定义的Sld 样式
|
||
//sld与mvt样式区别:sld针对一个要素多个rule的样式可以同时独立生效,而mvt只能给一个要素设置一个样式
|
||
|
||
;(function (window) {
|
||
function TaitanPbfStyle(options) {
|
||
this.ol = null
|
||
this.json = {}
|
||
this.options = {}
|
||
this.style = {}
|
||
this.iconCache = {}
|
||
this.properties = null
|
||
this.type = null
|
||
this.scale = null
|
||
this.resolution = null
|
||
this.filterIsPass = null
|
||
this.scaleIsPass = true
|
||
this.init(options)
|
||
}
|
||
|
||
TaitanPbfStyle.prototype = {
|
||
constructor: TaitanPbfStyle,
|
||
|
||
defaults: {
|
||
ol: null,
|
||
json: {},
|
||
},
|
||
|
||
init: function (options) {
|
||
let opts = Object.assign({}, this.defaults, options)
|
||
this.json = opts.json
|
||
this.ol = opts.ol
|
||
},
|
||
|
||
reset: function () {
|
||
this.options = {}
|
||
this.style = {}
|
||
this.properties = null
|
||
this.type = null
|
||
this.scale = null
|
||
this.resolution = null
|
||
this.filterIsPass = null
|
||
this.scaleIsPass = true
|
||
},
|
||
|
||
getStyle: function () {
|
||
let This = this
|
||
return function (feature, resolution) {
|
||
This.reset()
|
||
This.type = feature.getGeometry().getType()
|
||
This.properties = feature.getProperties()
|
||
This.resolution = resolution
|
||
This.scale = 1 / (0.0254 / (96 * resolution))
|
||
This.filter()
|
||
return This.buildStyle()
|
||
}
|
||
},
|
||
|
||
// 根据options组装最终的样式对象
|
||
buildStyle: function () {
|
||
if (Object.keys(this.options).length == 0) return [new this.ol.style.Style({})]
|
||
// if (JSON.stringify(this.options) == '{}') return;
|
||
|
||
// 组装基础样式
|
||
switch (this.type.toLowerCase()) {
|
||
case 'point':
|
||
if (this.options.image.imageStyle) this.style.image = this.options.image.imageStyle
|
||
break
|
||
|
||
case 'linestring':
|
||
if (this.options.stroke) this.style.stroke = this.options.stroke
|
||
break
|
||
|
||
case 'polygon':
|
||
if (this.options.fill) this.style.fill = this.options.fill
|
||
if (this.options.stroke) this.style.stroke = this.options.stroke
|
||
break
|
||
|
||
default:
|
||
break
|
||
}
|
||
|
||
// 组装label样式,这里控制了下显示层级,只有分辨率小于40的时候才会显示label,后续可以做成可配置项
|
||
if (this.resolution < 40 && this.options.text) {
|
||
this.style.text = new this.ol.style.Text({
|
||
text: this.options.text.text ? this.options.text.text + '' : '',
|
||
textBaseline: 'top',
|
||
overflow: 'line',
|
||
font: `${this.options.text.font || 10}px sans-serif`,
|
||
offsetX: this.options.text.offsetX,
|
||
offsetY: this.options.text.offsetY,
|
||
fill: this.options.text.fill,
|
||
stroke: this.options.text.stroke,
|
||
rotateWithView: false,
|
||
})
|
||
}
|
||
|
||
// 组装层级样式
|
||
this.style.zIndex = this.options.zIndex || 1
|
||
return [new this.ol.style.Style(this.style)]
|
||
},
|
||
|
||
// 构建样式options
|
||
buildOptions: function (rule) {
|
||
// rule.Symbolizer.WellKnownName = 'img';
|
||
// rule.Symbolizer.src = 'https://unpkg.com/@mapbox/maki@4.0.0/icons/park-15.svg';
|
||
|
||
this.options.image = {}
|
||
this.options.image.rotation = rule.Symbolizer.Rotation || 0
|
||
|
||
if (rule.Symbolizer.FillColor && rule.Symbolizer.WellKnownName != 'img') {
|
||
let imgFillColor = this.buildRgba(rule.Symbolizer.FillColor, rule.Symbolizer.FillOpacity)
|
||
this.options.image.fill = new this.ol.style.Fill({
|
||
color: imgFillColor,
|
||
})
|
||
|
||
if (rule.Symbolizer.StrokeColor) {
|
||
let imgStrokeColor = this.buildRgba(rule.Symbolizer.StrokeColor, rule.Symbolizer.StrokeOpacity)
|
||
this.options.image.stroke = new this.ol.style.Stroke({
|
||
color: imgStrokeColor,
|
||
width: rule.Symbolizer.StrokeWidth,
|
||
lineDash: rule.Symbolizer.StrokeDasharray.split(' '),
|
||
})
|
||
}
|
||
}
|
||
|
||
this.options.image.render = 'image'
|
||
|
||
switch (rule.Symbolizer.WellKnownName) {
|
||
case 'circle':
|
||
// 圆形
|
||
this.options.image.radius = rule.Symbolizer.Size
|
||
this.options.image.imageStyle = new this.ol.style.Circle(this.options.image)
|
||
break
|
||
|
||
case 'square':
|
||
// 正方形
|
||
this.options.image.radius = rule.Symbolizer.Size
|
||
this.options.image.points = 4
|
||
this.options.image.angle = Math.PI / 4
|
||
this.options.image.imageStyle = new this.ol.style.RegularShape(this.options.image)
|
||
break
|
||
|
||
case 'triangle':
|
||
// 三角形
|
||
this.options.image.radius = rule.Symbolizer.Size
|
||
this.options.image.points = 3
|
||
this.options.image.angle = 0
|
||
this.options.image.imageStyle = new this.ol.style.RegularShape(this.options.image)
|
||
break
|
||
|
||
case 'star':
|
||
// 五角形
|
||
this.options.image.radius = rule.Symbolizer.Size
|
||
this.options.image.points = 5
|
||
this.options.image.radius2 = rule.Symbolizer.Size / 2.5
|
||
this.options.image.angle = 0
|
||
this.options.image.imageStyle = new this.ol.style.RegularShape(this.options.image)
|
||
break
|
||
|
||
case 'cross':
|
||
// 十字架
|
||
this.options.image.radius = rule.Symbolizer.Size
|
||
this.options.image.points = 4
|
||
this.options.image.radius2 = 0
|
||
this.options.image.angle = 0
|
||
this.options.image.imageStyle = new this.ol.style.RegularShape(this.options.image)
|
||
break
|
||
|
||
case 'x':
|
||
// ×形
|
||
this.options.image.radius = rule.Symbolizer.Size
|
||
this.options.image.points = 4
|
||
this.options.image.radius2 = 0
|
||
this.options.image.angle = Math.PI / 4
|
||
this.options.image.imageStyle = new this.ol.style.RegularShape(this.options.image)
|
||
break
|
||
|
||
case 'img':
|
||
let icon = this.iconCache[rule.Symbolizer.src]
|
||
if (!icon) {
|
||
this.options.image.src = rule.Symbolizer.src
|
||
this.options.image.anchorXUnits = 'fraction'
|
||
this.options.image.anchorYUnits = 'fraction'
|
||
this.options.image.anchor = [0.5, 0.5]
|
||
this.options.image.offset = [0, 0]
|
||
this.options.image.scale = 1
|
||
this.options.image.rotation = rule.Symbolizer.Rotation
|
||
;(this.options.image.crossOrigin = 'anonymous'), (this.options.image.imageStyle = new this.ol.style.Icon(this.options.image))
|
||
this.iconCache[rule.Symbolizer.src] = this.options.image.imageStyle
|
||
} else {
|
||
this.options.image.imageStyle = icon
|
||
}
|
||
break
|
||
|
||
default:
|
||
break
|
||
}
|
||
|
||
// 构建option中的填充色
|
||
if (rule.Symbolizer.FillColor) {
|
||
let fillColor = this.buildRgba(rule.Symbolizer.FillColor, rule.Symbolizer.FillOpacity)
|
||
this.options.fill = new this.ol.style.Fill({
|
||
color: fillColor,
|
||
})
|
||
}
|
||
|
||
// 构建option中的边框
|
||
let strokeColor, strokeWidth, strokeDasharray
|
||
if (rule.Symbolizer.StrokeColor) {
|
||
strokeColor = this.buildRgba(rule.Symbolizer.StrokeColor, rule.Symbolizer.StrokeOpacity)
|
||
|
||
if (rule.Symbolizer.StrokeWidth && rule.Symbolizer.StrokeWidth > 1) {
|
||
strokeWidth = rule.Symbolizer.StrokeWidth
|
||
}
|
||
|
||
if (rule.Symbolizer.StrokeDasharray && rule.Symbolizer.StrokeDasharray.split(' ')) {
|
||
strokeDasharray = rule.Symbolizer.StrokeDasharray.split(' ')
|
||
}
|
||
|
||
this.options.stroke = new this.ol.style.Stroke({
|
||
color: strokeColor,
|
||
width: strokeWidth,
|
||
lineDash: strokeDasharray,
|
||
})
|
||
}
|
||
|
||
// 构建option中的标签
|
||
if (rule.Label && JSON.stringify(rule.Label) != '{}') {
|
||
this.options.text = {}
|
||
this.options.text.text = this.properties[rule.Label.PropertyName]
|
||
this.options.text.font = rule.Label.FontSize
|
||
this.options.text.offsetX = rule.Label.AnchorPointX
|
||
this.options.text.offsetY = rule.Label.AnchorPointY
|
||
|
||
if (rule.Label.FillColor) {
|
||
let fillColor = this.buildRgba(rule.Label.FillColor, rule.Label.FillOpacity)
|
||
this.options.text.fill = new this.ol.style.Fill({
|
||
color: fillColor,
|
||
})
|
||
}
|
||
|
||
if (rule.Label.HaloFillColor) {
|
||
let haloFillColor = this.buildRgba(rule.Label.HaloFillColor, rule.Label.HaloFillOpacity)
|
||
this.options.text.stroke = new this.ol.style.Stroke({
|
||
color: haloFillColor,
|
||
width: rule.Label.HaloRadius,
|
||
})
|
||
}
|
||
}
|
||
},
|
||
|
||
// 根据属性和比例条件进行判断
|
||
filter: function () {
|
||
for (let rule of this.json.Rule) {
|
||
// 每次循环开始前将重置filterIsPass参数
|
||
this.filterIsPass = undefined
|
||
|
||
// 判断属性过滤条件,如果属性过滤条件参数不存在或者为空,则不做进一步的判断,直接赋值true
|
||
if (
|
||
rule.Filter instanceof Object == false ||
|
||
JSON.stringify(rule.Filter) == '{}' ||
|
||
rule.Filter.Conditions == undefined ||
|
||
rule.Filter.Conditions.length == 0
|
||
) {
|
||
this.filterIsPass = true
|
||
} else {
|
||
this.filterConditions(rule.Filter)
|
||
}
|
||
|
||
this.buildOptions(rule)
|
||
|
||
// 判断比例条件是否符合
|
||
// let minScale = rule.MinScaleDenominator ? rule.MinScaleDenominator : 10;
|
||
// let maxScale = rule.MaxScaleDenominator ? rule.MaxScaleDenominator : 10000000000;
|
||
// this.scaleIsPass = (this.scale < minScale || this.scale > maxScale) ? false : true;
|
||
// if (this.filterIsPass && this.scaleIsPass) this.buildOptions(rule);
|
||
}
|
||
},
|
||
|
||
// 根据属性过滤条件进行判断
|
||
filterConditions: function (filter) {
|
||
for (let condition of filter.Conditions) {
|
||
let bool,
|
||
val = this.properties[condition.PropertyName] + '' || ''
|
||
switch (condition.Condition) {
|
||
case 'PropertyIsEqualTo':
|
||
bool = val == condition.Literal
|
||
break
|
||
|
||
case 'PropertyIsNotEqualTo':
|
||
bool = val != condition.Literal
|
||
break
|
||
|
||
case 'PropertyIsLike':
|
||
bool = val.indexOf(condition.Literal) > -1
|
||
break
|
||
|
||
case 'PropertyIsBetween':
|
||
let num = parseFloat(val)
|
||
bool = num >= condition.LowerBoundary.Literal && num <= condition.UpperBoundary.Literal
|
||
break
|
||
|
||
default:
|
||
break
|
||
}
|
||
|
||
if (filter.FilterType == 'And') {
|
||
this.filterIsPass = this.filterIsPass == undefined ? bool : this.filterIsPass && bool
|
||
} else if (filter.FilterType == 'Or') {
|
||
this.filterIsPass = this.filterIsPass == undefined ? bool : this.filterIsPass || bool
|
||
}
|
||
}
|
||
},
|
||
|
||
hexToRgb: function (hex) {
|
||
let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||
return result
|
||
? {
|
||
r: parseInt(result[1], 16),
|
||
g: parseInt(result[2], 16),
|
||
b: parseInt(result[3], 16),
|
||
}
|
||
: null
|
||
},
|
||
|
||
buildRgba: function (hex, opa) {
|
||
hex = this.hexToRgb(hex)
|
||
if (hex == 'null') return ''
|
||
return `rgba(${hex.r},${hex.g},${hex.b},${opa})`
|
||
},
|
||
}
|
||
|
||
//对外接口
|
||
window.mars3d.TaitanPbfStyle = TaitanPbfStyle
|
||
})(window)
|