ol-ext
Version:
A set of cool extensions for OpenLayers (ol) in node modules structure
293 lines (273 loc) • 9.49 kB
JavaScript
/* Copyright (c) 2018 Jean-Marc VIGLINO,
released under the CeCILL-B license (French BSD license)
(http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/
import {DEVICE_PIXEL_RATIO as ol_has_DEVICE_PIXEL_RATIO} from 'ol/has.js'
import ol_style_Stroke from 'ol/style/Stroke.js'
import {asString as ol_color_asString} from 'ol/color.js'
import ol_style_FillPattern from './FillPattern.js'
/**
* @classdesc
* Stroke style with named pattern
*
* @constructor
* @param {any} options
* @param {ol.style.Image|undefined} options.image an image pattern, image must be preloaded to draw on first call
* @param {number|undefined} options.opacity opacity with image pattern, default:1
* @param {string} options.pattern pattern name (override by image option)
* @param {ol.colorLike} options.color pattern color
* @param {ol.style.Fill} options.fill fill color (background)
* @param {number|Array<number>} options.offset pattern offset for hash/dot/circle/cross pattern
* @param {number} options.size line size for hash/dot/circle/cross pattern
* @param {number} options.spacing spacing for hash/dot/circle/cross pattern
* @param {number|bool} options.angle angle for hash pattern / true for 45deg dot/circle/cross
* @param {number} options.scale pattern scale
* @extends {ol.style.Fill}
* @implements {ol.structs.IHasChecksum}
* @api
*/
var ol_style_StrokePattern = class olstyleStrokePattern extends ol_style_Stroke {
constructor(options) {
super(options)
options = options || {}
var pattern, i;
var canvas = this.canvas_ = document.createElement('canvas')
var scale = Number(options.scale) > 0 ? Number(options.scale) : 1
var ratio = scale * ol_has_DEVICE_PIXEL_RATIO || ol_has_DEVICE_PIXEL_RATIO
var ctx = canvas.getContext('2d')
if (options.image) {
options.image.load()
var img = options.image.getImage()
if (img.width) {
canvas.width = Math.round(img.width * ratio)
canvas.height = Math.round(img.height * ratio)
ctx.globalAlpha = typeof (options.opacity) == 'number' ? options.opacity : 1
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
pattern = ctx.createPattern(canvas, 'repeat')
} else {
var self = this
pattern = [0, 0, 0, 0]
img.onload = function () {
canvas.width = Math.round(img.width * ratio)
canvas.height = Math.round(img.height * ratio)
ctx.globalAlpha = typeof (options.opacity) == 'number' ? options.opacity : 1
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
pattern = ctx.createPattern(canvas, 'repeat')
self.setColor(pattern)
}
}
} else {
var pat = this.getPattern_(options)
canvas.width = Math.round(pat.width * ratio)
canvas.height = Math.round(pat.height * ratio)
ctx.beginPath()
if (options.fill) {
ctx.fillStyle = ol_color_asString(options.fill.getColor())
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
ctx.scale(ratio, ratio)
ctx.lineCap = "round"
ctx.lineWidth = pat.stroke || 1
ctx.fillStyle = ol_color_asString(options.color || "#000")
ctx.strokeStyle = ol_color_asString(options.color || "#000")
if (pat.circles)
for (i = 0; i < pat.circles.length; i++) {
var ci = pat.circles[i]
ctx.beginPath()
ctx.arc(ci[0], ci[1], ci[2], 0, 2 * Math.PI)
if (pat.fill)
ctx.fill()
if (pat.stroke)
ctx.stroke()
}
if (!pat.repeat)
pat.repeat = [[0, 0]]
if (pat.char) {
ctx.font = pat.font || (pat.width) + "px Arial"
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
if (pat.angle) {
ctx.fillText(pat.char, pat.width / 4, pat.height / 4)
ctx.fillText(pat.char, 5 * pat.width / 4, 5 * pat.height / 4)
ctx.fillText(pat.char, pat.width / 4, 5 * pat.height / 4)
ctx.fillText(pat.char, 5 * pat.width / 4, pat.height / 4)
ctx.fillText(pat.char, 3 * pat.width / 4, 3 * pat.height / 4)
ctx.fillText(pat.char, -pat.width / 4, -pat.height / 4)
ctx.fillText(pat.char, 3 * pat.width / 4, -pat.height / 4)
ctx.fillText(pat.char, -pat.width / 4, 3 * pat.height / 4)
}
else
ctx.fillText(pat.char, pat.width / 2, pat.height / 2)
}
if (pat.lines)
for (i = 0; i < pat.lines.length; i++)
for (var r = 0; r < pat.repeat.length; r++) {
var li = pat.lines[i]
ctx.beginPath()
ctx.moveTo(li[0] + pat.repeat[r][0], li[1] + pat.repeat[r][1])
for (var k = 2; k < li.length; k += 2) {
ctx.lineTo(li[k] + pat.repeat[r][0], li[k + 1] + pat.repeat[r][1])
}
if (pat.fill)
ctx.fill()
if (pat.stroke)
ctx.stroke()
ctx.save()
ctx.strokeStyle = 'red'
ctx.strokeWidth = 0.1
//ctx.strokeRect(0,0,canvas.width,canvas.height);
ctx.restore()
}
pattern = ctx.createPattern(canvas, 'repeat')
if (options.offset) {
var offset = options.offset
if (typeof (offset) == "number")
offset = [offset, offset]
if (offset instanceof Array) {
var dx = Math.round((offset[0] * ratio))
var dy = Math.round((offset[1] * ratio))
// New pattern
ctx.scale(1 / ratio, 1 / ratio)
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.translate(dx, dy)
ctx.fillStyle = pattern
ctx.fillRect(-dx, -dy, canvas.width, canvas.height)
pattern = ctx.createPattern(canvas, 'repeat')
}
}
}
this.setColor (pattern);
}
/**
* Clones the style.
* @return {ol_style_StrokePattern}
*/
clone() {
var s = super.clone()
s.canvas_ = this.canvas_
return s
}
/** Get canvas used as pattern
* @return {canvas}
*/
getImage() {
return this.canvas_
}
/** Get pattern
* @param {olx.style.FillPatternOption}
*/
getPattern_(options) {
var pat = ol_style_FillPattern.patterns[options.pattern]
|| ol_style_FillPattern.patterns.dot
var d = Math.round(options.spacing) || 10
var size
// var d2 = Math.round(d/2)+0.5;
switch (options.pattern) {
case 'dot':
case 'circle':
{
size = options.size === 0 ? 0 : options.size / 2 || 2
if (!options.angle) {
pat.width = pat.height = d
pat.circles = [[d / 2, d / 2, size]]
if (options.pattern == 'circle') {
pat.circles = pat.circles.concat([
[d / 2 + d, d / 2, size],
[d / 2 - d, d / 2, size],
[d / 2, d / 2 + d, size],
[d / 2, d / 2 - d, size],
[d / 2 + d, d / 2 + d, size],
[d / 2 + d, d / 2 - d, size],
[d / 2 - d, d / 2 + d, size],
[d / 2 - d, d / 2 - d, size]
])
}
}
else {
d = pat.width = pat.height = Math.round(d * 1.4)
pat.circles = [[d / 4, d / 4, size], [3 * d / 4, 3 * d / 4, size]]
if (options.pattern == 'circle') {
pat.circles = pat.circles.concat([
[d / 4 + d, d / 4, size],
[d / 4, d / 4 + d, size],
[3 * d / 4 - d, 3 * d / 4, size],
[3 * d / 4, 3 * d / 4 - d, size],
[d / 4 + d, d / 4 + d, size],
[3 * d / 4 - d, 3 * d / 4 - d, size]
])
}
}
break
}
case 'tile':
case 'square':
{
size = options.size === 0 ? 0 : options.size / 2 || 2
if (!options.angle) {
pat.width = pat.height = d
pat.lines = [[d / 2 - size, d / 2 - size, d / 2 + size, d / 2 - size, d / 2 + size, d / 2 + size, d / 2 - size, d / 2 + size, d / 2 - size, d / 2 - size]]
}
else {
pat.width = pat.height = d
//size *= Math.sqrt(2);
pat.lines = [[d / 2 - size, d / 2, d / 2, d / 2 - size, d / 2 + size, d / 2, d / 2, d / 2 + size, d / 2 - size, d / 2]]
}
if (options.pattern == 'square')
pat.repeat = [[0, 0], [0, d], [d, 0], [0, -d], [-d, 0], [-d, -d], [d, d], [-d, d], [d, -d]]
break
}
case 'cross':
{ // Limit angle to 0 | 45
if (options.angle)
options.angle = 45
}
// fallthrough
case 'hatch':
{
var a = Math.round(((options.angle || 0) - 90) % 360)
if (a > 180)
a -= 360
a *= Math.PI / 180
var cos = Math.cos(a)
var sin = Math.sin(a)
if (Math.abs(sin) < 0.0001) {
pat.width = pat.height = d
pat.lines = [[0, 0.5, d, 0.5]]
pat.repeat = [[0, 0], [0, d]]
}
else if (Math.abs(cos) < 0.0001) {
pat.width = pat.height = d
pat.lines = [[0.5, 0, 0.5, d]]
pat.repeat = [[0, 0], [d, 0]]
if (options.pattern == 'cross') {
pat.lines.push([0, 0.5, d, 0.5])
pat.repeat.push([0, d])
}
}
else {
var w = pat.width = Math.round(Math.abs(d / sin)) || 1
var h = pat.height = Math.round(Math.abs(d / cos)) || 1
if (options.pattern == 'cross') {
pat.lines = [[-w, -h, 2 * w, 2 * h], [2 * w, -h, -w, 2 * h]]
pat.repeat = [[0, 0]]
}
else if (cos * sin > 0) {
pat.lines = [[-w, -h, 2 * w, 2 * h]]
pat.repeat = [[0, 0], [w, 0], [0, h]]
}
else {
pat.lines = [[2 * w, -h, -w, 2 * h]]
pat.repeat = [[0, 0], [-w, 0], [0, h]]
}
}
pat.stroke = options.size === 0 ? 0 : options.size || 4
break
}
default: {
break
}
}
return pat
}
}
export default ol_style_StrokePattern