2019-11-04 10:59:08 +01:00
|
|
|
import { Points } from '../../utils.js'
|
|
|
|
import { EventHandler } from './utils.js'
|
|
|
|
import { FlagPolygon } from '../graphics/label.js'
|
|
|
|
import { DeepZoomMap } from './map.js'
|
2020-01-09 15:14:27 +01:00
|
|
|
import { PIXIUtils } from './utils.js'
|
2019-11-04 10:59:08 +01:00
|
|
|
|
|
|
|
/**
|
2019-12-11 15:29:59 +01:00
|
|
|
* * GeoGraphics are graphical objects, that does not store the graphics information
|
2019-11-04 10:59:08 +01:00
|
|
|
* in screen space, but in geographical coordinates. Therefore GeoGraphics must be
|
|
|
|
* placed on GeoLayers to work properly.
|
|
|
|
*
|
|
|
|
* (Note: As GeoLayers are always children of a map layer. When the map is changed
|
|
|
|
* all GeoLayers are notified via the 'adaptTo(map)' method.)
|
|
|
|
*
|
|
|
|
* The geolayers forward this 'adaptTo' to all children that are GeoGraphics.
|
|
|
|
* Which adjust their so called 'point' data to the new map.
|
|
|
|
*
|
2019-12-11 15:29:59 +01:00
|
|
|
* @export
|
|
|
|
* @class GeoGraphics
|
2019-11-04 10:59:08 +01:00
|
|
|
*/
|
|
|
|
export class GeoGraphics {
|
|
|
|
constructor(coordinates, { scale = 1, onDraw = null, onDrawEnd = null, debug = false } = {}) {
|
2019-12-11 15:29:59 +01:00
|
|
|
this._coordinates = coordinates
|
2019-11-04 10:59:08 +01:00
|
|
|
this.debug = debug
|
|
|
|
this.graphics = new PIXI.Graphics()
|
|
|
|
this.scale = scale
|
|
|
|
this.drawHandler = new EventHandler('onDraw', { listeners: onDraw })
|
|
|
|
this.drawEndHandler = new EventHandler('onDrawEnd', { listeners: onDrawEnd })
|
|
|
|
this._points = null
|
|
|
|
this._position = null
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
/**
|
|
|
|
* The coordinates of the geographics.
|
|
|
|
*
|
|
|
|
* @member {array}
|
|
|
|
* @readonly
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
|
|
|
get coordinates() {
|
|
|
|
return this._coordinates
|
|
|
|
}
|
|
|
|
|
2019-11-04 10:59:08 +01:00
|
|
|
clone() {
|
|
|
|
console.error(`Call of abstract method clone(). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
|
|
|
|
_cloneOptions() {
|
|
|
|
return {
|
|
|
|
debug: this.debug,
|
|
|
|
scale: this.scale
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The _adaptCoordinates is called first by the adaptTo Method.
|
|
|
|
* Here all coordinates are transformed into point coordinates.
|
|
|
|
* This must be overloaded in subclass.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
*/
|
|
|
|
_adaptCoordinates(map) {
|
|
|
|
console.error(`Call of abstract method _adaptCoordinates(map). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets all screen points in a single array.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
* @returns {array} - Array of all points in the GeoGraphic.
|
|
|
|
*/
|
|
|
|
_getPoints() {
|
|
|
|
console.error(`Call of abstract method _getPoints(func). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manipulates all points depending on a function.
|
|
|
|
* Mainly used to transform points to local space.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
* @param {function} func
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
|
|
|
_manipulatePoints(func) {
|
|
|
|
console.error(`Call of abstract method _manipulatePoints(func). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The _draw method is called last on adaptation. It creates the GraphicData
|
|
|
|
* of the specified subclass. To manipulate the style of the graphic, hook an onDraw listener
|
|
|
|
* to the GeoGraphics object. It is called before the _draw and lets the user modify color and
|
|
|
|
* lineStyle of the drawn object.
|
|
|
|
*
|
|
|
|
* Note: It could also be used for more radical manipulations on the graphics object.
|
|
|
|
* But this should be used with care.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
*/
|
|
|
|
_draw() {
|
|
|
|
console.error(`Call of abstract method _draw(). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called by the containing geo layer, when the map changes.
|
|
|
|
*/
|
|
|
|
adaptTo(map) {
|
|
|
|
this._points = this._adaptCoordinates(map)
|
|
|
|
this._updatePosition()
|
|
|
|
this.draw()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redraws the graphics.
|
2019-11-04 18:20:32 +01:00
|
|
|
*
|
2019-11-04 10:59:08 +01:00
|
|
|
* This should be only called if you require an redraw independent of an adapt.
|
|
|
|
*
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
|
|
|
draw() {
|
|
|
|
this._prepareDraw()
|
|
|
|
this.drawHandler.call(this, this.graphics)
|
|
|
|
this._draw()
|
|
|
|
this.drawEndHandler.call(this, this.graphics)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the point data.
|
|
|
|
* Note: This data changes on adaptation.
|
|
|
|
*/
|
|
|
|
get points() {
|
|
|
|
return this._points
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the screen position of the GeoGraphics.
|
|
|
|
*/
|
|
|
|
get position() {
|
|
|
|
return this._position
|
|
|
|
}
|
|
|
|
|
2019-11-13 12:42:06 +01:00
|
|
|
get layer() {
|
|
|
|
return this._layer ? this._layer : null
|
|
|
|
}
|
|
|
|
|
|
|
|
setLayer(layer) {
|
|
|
|
this._layer = layer
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
/**
|
|
|
|
* Map of the containing layer. Null if on no layer.
|
|
|
|
*
|
|
|
|
* @readonly
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
2019-11-20 15:59:10 +01:00
|
|
|
get map() {
|
|
|
|
let map = null
|
|
|
|
if (this.mapLayer) {
|
|
|
|
map = this.mapLayer.map
|
|
|
|
}
|
|
|
|
return map
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
/**
|
|
|
|
* MapLayer of the containing layer. Null if on no layer.
|
|
|
|
*
|
|
|
|
* @member {MapLayer}
|
|
|
|
* @readonly
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
2019-11-20 15:59:10 +01:00
|
|
|
get mapLayer() {
|
|
|
|
let mapLayer = null
|
|
|
|
if (this.layer) {
|
|
|
|
mapLayer = this.layer.mapLayer
|
|
|
|
}
|
|
|
|
return mapLayer
|
|
|
|
}
|
2019-11-04 10:59:08 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare draw is a private function, that prepares the graphics
|
|
|
|
* for the next draw call. It also fires the drawHandler.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
|
|
|
_prepareDraw() {
|
|
|
|
this.graphics.clear()
|
|
|
|
/*
|
|
|
|
Set a fillcolor and a stroke style for
|
|
|
|
debugging. Can be overloaded using the onDraw
|
|
|
|
event function.
|
|
|
|
*/
|
|
|
|
if (this.debug) this.graphics.beginFill(0xff00ff)
|
|
|
|
}
|
|
|
|
|
|
|
|
_updatePosition() {
|
|
|
|
let points = this._getPoints()
|
|
|
|
this._position = GeoGraphics.calculateCenterOfMass(points)
|
|
|
|
this._manipulatePoints(point => {
|
|
|
|
point = Points.subtract(point, this._position)
|
|
|
|
return point
|
|
|
|
})
|
|
|
|
this.graphics.position = this._position
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the center of Mass for a set of points.
|
|
|
|
*
|
|
|
|
* @static
|
|
|
|
* @param {Array} points - Array of points in the format {x: a, y:b}
|
|
|
|
* @returns {object} - Returns a point containing the center of mass of the polygon.
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
|
|
|
static calculateCenterOfMass(points) {
|
|
|
|
let com = new PIXI.Point()
|
|
|
|
points.forEach(p => {
|
|
|
|
let point = new PIXI.Point(p.x, p.y)
|
|
|
|
com = Points.add(com, point)
|
|
|
|
})
|
|
|
|
return Points.multiplyScalar(com, 1 / points.length)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a single point on the Map.
|
|
|
|
*
|
|
|
|
* This GeoGraphics does not provide any visual representation.
|
|
|
|
* Draw the desired shape in the onDraw callback.
|
2019-12-11 15:29:59 +01:00
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @class GeoPoint
|
|
|
|
* @extends {GeoGraphics}
|
2019-11-04 10:59:08 +01:00
|
|
|
*/
|
|
|
|
export class GeoPoint extends GeoGraphics {
|
|
|
|
clone() {
|
|
|
|
return new GeoPoint(this.coordinates, this._cloneOptions())
|
|
|
|
}
|
|
|
|
|
|
|
|
_adaptCoordinates(map) {
|
|
|
|
let scale = 1
|
|
|
|
|
2019-11-04 18:20:32 +01:00
|
|
|
if (map instanceof DeepZoomMap) {
|
|
|
|
scale = map.image.scale.x
|
2019-11-04 10:59:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
scale = scale / 4
|
|
|
|
|
|
|
|
return map.coordinatesToPoint(this.coordinates)
|
|
|
|
}
|
|
|
|
|
|
|
|
_getPoints() {
|
|
|
|
return [this.points]
|
|
|
|
}
|
|
|
|
|
|
|
|
_manipulatePoints(func) {
|
|
|
|
this._points = func(this._points)
|
|
|
|
}
|
|
|
|
|
|
|
|
_draw() {}
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
/**
|
|
|
|
* Represensts a line between two locations.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @class GeoLine
|
|
|
|
* @extends {GeoGraphics}
|
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
export class GeoLine extends GeoGraphics {
|
|
|
|
/**
|
|
|
|
* @param {object} opts - Optional values
|
|
|
|
* @param {array} [opts.points=[]] - Initial points of the geo shape.
|
|
|
|
* @param {boolean} [closed=false] - Defines if the
|
|
|
|
*/
|
|
|
|
constructor(coordinates, { closed = false, size = 1, onDraw = null } = {}) {
|
|
|
|
super(coordinates, {
|
|
|
|
size,
|
|
|
|
onDraw
|
|
|
|
})
|
|
|
|
|
|
|
|
this._closed = closed
|
|
|
|
}
|
|
|
|
|
|
|
|
clone() {
|
|
|
|
return new GeoLine(this.coordinates, this._cloneOptions)
|
|
|
|
}
|
|
|
|
|
|
|
|
_cloneOptions() {
|
|
|
|
let options = super._cloneOptions()
|
|
|
|
Object.assign(options, {
|
|
|
|
closed: this.closed
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a point to the geo line.
|
|
|
|
*/
|
|
|
|
addPoint(coordinate) {
|
|
|
|
this.coordinates.push(coordinate)
|
|
|
|
}
|
|
|
|
|
|
|
|
_manipulatePoints(func) {
|
|
|
|
this.points.forEach((point, idx, array) => {
|
|
|
|
array[idx] = func(point)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
_getPoints() {
|
|
|
|
return this.points
|
|
|
|
}
|
|
|
|
|
|
|
|
_adaptCoordinates(map) {
|
|
|
|
let points = []
|
|
|
|
this.coordinates.forEach(point => {
|
|
|
|
points.push(map.coordinatesToPoint(point))
|
|
|
|
})
|
|
|
|
return points
|
|
|
|
}
|
|
|
|
|
|
|
|
_prepareDraw() {
|
|
|
|
this.graphics.clear()
|
|
|
|
if (this.debug) this.graphics.lineStyle(0.5, 0xff00ff)
|
|
|
|
this.drawHandler.call(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
_draw() {
|
|
|
|
/**
|
|
|
|
* This resets the fill.
|
|
|
|
*
|
|
|
|
* DISCUSS: SO: "I'm not sure how the line should be defined.
|
|
|
|
* On the one hand. The line is clearly intended to
|
|
|
|
* represent a line and not an area. On the other hand,
|
|
|
|
* why should the user be prevented from using a fill for the
|
|
|
|
* area within the line. But if he want's a fill, why don't take
|
|
|
|
* a Polygon in the first place?
|
|
|
|
*
|
|
|
|
* (But if it's a predefined GeoJSON object obtained through e.g. elasticsearch,
|
|
|
|
* then the user is not in full control of the object type and it may be a good
|
|
|
|
* addition to grant the user this additional design choice.)
|
|
|
|
*
|
|
|
|
* The opportunity to do so would result in additional conditions, when creating the
|
|
|
|
* GeoGraphics of an overlay."
|
|
|
|
* */
|
|
|
|
this.graphics.beginFill(0, 0)
|
|
|
|
|
|
|
|
if (this.points.length > 0) {
|
|
|
|
this.graphics.moveTo(this.points[0].x, this.points[0].y)
|
|
|
|
|
|
|
|
for (let i = 1; i < this.points.length; i++) {
|
|
|
|
this.graphics.lineTo(this.points[i].x, this.points[i].y)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.closed) {
|
|
|
|
this.graphics.lineTo(this.points[0].x, this.points[0].y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get closed() {
|
|
|
|
return this._closed
|
|
|
|
}
|
|
|
|
|
|
|
|
set closed(val) {
|
|
|
|
if (val != this._closed) {
|
|
|
|
this._closed = val
|
|
|
|
this.draw()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
/**
|
|
|
|
* Represents a shape on a map.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @class GeoShape
|
|
|
|
* @extends {GeoGraphics}
|
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
export class GeoShape extends GeoGraphics {
|
|
|
|
clone() {
|
|
|
|
return new GeoShape(this.coordinates, this._cloneOptions)
|
|
|
|
}
|
|
|
|
|
|
|
|
_manipulatePoints(func) {
|
|
|
|
this.constructor._manipulatePoints(this.points, func)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mets the requirements for the _manipulatePointsMethod().
|
|
|
|
* Primarily used in subclasses to get the processing steps from
|
|
|
|
* their superclass.
|
|
|
|
*
|
|
|
|
* @static
|
|
|
|
* @protected
|
|
|
|
* @param {Array.<PIXI.Points>} points - The points array that shold be manipulated.
|
|
|
|
* @param {function} func - The function that changes the single point value. Has to return a new point.
|
|
|
|
* @memberof GeoShape
|
|
|
|
*/
|
|
|
|
static _manipulatePoints(points, func) {
|
|
|
|
points.forEach((pointArray, arrIdx) => {
|
|
|
|
pointArray.forEach((point, idx) => {
|
|
|
|
points[arrIdx][idx] = func(point)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
_getPoints() {
|
|
|
|
return this.constructor._getPointsFrom(this.points)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns all points of a polygon array.
|
|
|
|
* Useful for when getting sub polygons in child class.
|
|
|
|
*/
|
|
|
|
static _getPointsFrom(shape) {
|
|
|
|
let concatArray = []
|
|
|
|
shape.forEach(array => {
|
|
|
|
concatArray = concatArray.concat(array)
|
|
|
|
})
|
|
|
|
|
|
|
|
return concatArray
|
|
|
|
}
|
|
|
|
|
|
|
|
_adaptCoordinates(map) {
|
|
|
|
let val = this.constructor._adaptPoint(this.coordinates, map)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
|
|
|
static _adaptPoint(coordinates, map) {
|
|
|
|
return coordinates.map(array => {
|
|
|
|
return array.map(point => {
|
|
|
|
return map.coordinatesToPoint(point)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
_draw() {
|
|
|
|
this._drawFrom(this.points)
|
|
|
|
this.graphics.position = this.position
|
|
|
|
}
|
|
|
|
|
|
|
|
_drawFrom(shape) {
|
|
|
|
const { polygon, hole } = this.constructor._pointsToShape(shape)
|
|
|
|
this._drawShape(polygon, hole)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Separates the points array into it's two parts:
|
|
|
|
* - the solid polygon
|
|
|
|
* - a hole that is cut into the polygon (optional)
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {object} - Returns an object containing the polygon and the hole in the form of {polygon: [...PIXI.Point], hole: [...PIXI.Point]}
|
|
|
|
*/
|
|
|
|
static _pointsToShape(points) {
|
|
|
|
let polygon = GeoShape._transformToPIXI(points[0])
|
|
|
|
let hole = points[1] ? GeoShape._transformToPIXI(points[1]) : []
|
|
|
|
return { polygon, hole }
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transform an array of poins into an array of PIXI.Points.
|
|
|
|
* Note: When creating PIXI.Polygons, for some reason the points
|
|
|
|
* need to be a PIXI.Points object (at least the first one).
|
|
|
|
*
|
|
|
|
* @param {array} points - Points in the form of {x:a,y:b}
|
|
|
|
* @returns An array of PIXI.Points
|
|
|
|
* @memberof GeoPolygon
|
|
|
|
*/
|
|
|
|
static _transformToPIXI(points = []) {
|
|
|
|
let polygon = []
|
|
|
|
points.forEach(point => {
|
|
|
|
polygon.push(new PIXI.Point(point.x, point.y))
|
|
|
|
})
|
|
|
|
|
|
|
|
return polygon
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws a single shape onto the graphics object.
|
|
|
|
* Useful when being called from subclass.
|
|
|
|
*
|
|
|
|
* @protected
|
|
|
|
* @param {Array.<PIXI.Point>} polygon - An array of PIXI.Points for drawing a polygon.
|
|
|
|
* @param {Array.<PIXI.Point>} [hole=[]] - An Array of PIXI.Points for cutting a hole into the polygon
|
|
|
|
* @memberof GeoShape
|
|
|
|
*/
|
|
|
|
_drawShape(polygon, hole = []) {
|
|
|
|
// We save the fill specified in the onDraw event handler.
|
|
|
|
let { fill, alpha } = PIXIUtils.saveFill(this.graphics)
|
|
|
|
|
|
|
|
/**
|
2019-12-09 14:27:32 +01:00
|
|
|
* a) Draw the hole with a polygon.
|
2019-12-11 15:29:59 +01:00
|
|
|
*
|
2019-12-09 14:27:32 +01:00
|
|
|
* This may seem redundant to (c), but it's required (in this order(!))
|
2019-11-04 10:59:08 +01:00
|
|
|
* to make the hole clickable.
|
|
|
|
*
|
|
|
|
* It was a bit confusing, so I made a CodePen
|
|
|
|
* for it: https://codepen.io/Ukmasmu/pen/WJEaoK
|
|
|
|
*/
|
|
|
|
if (hole.length > 0) {
|
|
|
|
this.graphics.beginFill(0x0000ff, 0.0000001)
|
|
|
|
this.graphics.drawPolygon(hole)
|
|
|
|
}
|
|
|
|
|
2019-12-09 14:27:32 +01:00
|
|
|
/**
|
|
|
|
* b) Draw the shape.
|
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
this.graphics.beginFill(fill, alpha)
|
|
|
|
this.graphics.drawPolygon(polygon)
|
|
|
|
|
2019-12-09 14:27:32 +01:00
|
|
|
/**
|
|
|
|
* c) Add the hole.
|
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
if (hole.length > 0) {
|
|
|
|
this.graphics.beginHole()
|
|
|
|
this.graphics.drawPolygon(hole)
|
|
|
|
this.graphics.endHole()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The MultiGraphics makes use of the other GeoGraphics to
|
|
|
|
* create a single graphics element out of multiple different
|
|
|
|
* GeoGraphics.
|
2019-12-11 15:29:59 +01:00
|
|
|
*
|
|
|
|
* @class GeoMultiGraphics
|
|
|
|
* @extends {GeoGraphics}
|
2019-11-04 10:59:08 +01:00
|
|
|
*/
|
|
|
|
class GeoMultiGraphics extends GeoGraphics {
|
|
|
|
/**
|
|
|
|
* The _adaptCoordinates is called first by the adaptTo Method.
|
|
|
|
* Here all coordinates are transformed into point coordinates.
|
|
|
|
* This must be overloaded in subclass.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
*/
|
|
|
|
_adaptCoordinates(map) {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets all screen points in a single array.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
* @returns {array} - Array of all points in the GeoGraphic.
|
|
|
|
*/
|
|
|
|
_getPoints() {
|
|
|
|
console.error(`Call of abstract method _getPoints(func). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manipulates all points depending on a function.
|
|
|
|
* Mainly used to transform points to local space.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
* @param {function} func
|
|
|
|
* @memberof GeoGraphics
|
|
|
|
*/
|
|
|
|
_manipulatePoints(func) {
|
|
|
|
console.error(`Call of abstract method _manipulatePoints(func). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The _draw method is called last on adaptation. It creates the GraphicData
|
|
|
|
* of the specified subclass. To manipulate the style of the graphic, hook an onDraw listener
|
|
|
|
* to the GeoGraphics object. It is called before the _draw and lets the user modify color and
|
|
|
|
* lineStyle of the drawn object.
|
|
|
|
*
|
|
|
|
* Note: It could also be used for more radical manipulations on the graphics object.
|
|
|
|
* But this should be used with care.
|
|
|
|
*
|
|
|
|
* @abstract
|
|
|
|
*/
|
|
|
|
_draw() {
|
|
|
|
console.error(`Call of abstract method _draw(). Overwrite in subclass.`, this)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 14:27:32 +01:00
|
|
|
/**
|
2019-12-11 15:29:59 +01:00
|
|
|
* Text that is attatched to a GeoPoint.
|
2019-12-09 14:27:32 +01:00
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @class GeoText
|
|
|
|
* @extends {GeoPoint}
|
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
export class GeoText extends GeoPoint {
|
|
|
|
constructor(coordinates, text, opts) {
|
|
|
|
super(coordinates, opts)
|
|
|
|
this.align = opts.align
|
|
|
|
this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle)
|
|
|
|
this._text = new PIXI.Text(text, this.textStyle)
|
|
|
|
|
|
|
|
switch (this.align) {
|
|
|
|
case 'left':
|
|
|
|
break
|
|
|
|
case 'center':
|
|
|
|
this.text.position.x -= this.text.width / 2
|
|
|
|
break
|
|
|
|
case 'right':
|
|
|
|
this.text.position.x -= this.text.width
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
console.error('Align type is not implemented: ' + this.align + ". Use 'left', 'right' or 'center'.")
|
|
|
|
}
|
|
|
|
this.text.position.y -= this.text.height / 2
|
|
|
|
this.graphics.addChild(this.text)
|
|
|
|
}
|
|
|
|
|
|
|
|
get text() {
|
|
|
|
return this._text
|
|
|
|
}
|
|
|
|
|
|
|
|
_draw() {
|
|
|
|
super._draw()
|
|
|
|
}
|
|
|
|
|
|
|
|
adaptTo(map) {
|
|
|
|
super.adaptTo(map)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A geotext with a notch at a certain Position.
|
2019-12-11 15:29:59 +01:00
|
|
|
*
|
|
|
|
* TODO: This is a specialization for the Tüsch. This should be inside the Tüsch project.
|
2019-11-04 10:59:08 +01:00
|
|
|
*/
|
|
|
|
export class GeoFlagLabel extends GeoText {
|
|
|
|
constructor(coordinates, text, opts) {
|
|
|
|
super(
|
|
|
|
coordinates,
|
|
|
|
text,
|
|
|
|
Object.assign(opts, {
|
|
|
|
align: 'left'
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
if (!opts.flag) {
|
|
|
|
opts.flag = {}
|
|
|
|
}
|
|
|
|
|
|
|
|
const {
|
|
|
|
alpha = 1,
|
|
|
|
color = 0xffffff,
|
|
|
|
borderAlpha = 1,
|
|
|
|
borderColor = 0x000000,
|
|
|
|
borderThickness = 0,
|
|
|
|
fillNotch = false
|
|
|
|
} = opts
|
|
|
|
|
|
|
|
Object.assign(this, {
|
|
|
|
alpha,
|
|
|
|
color,
|
|
|
|
fillNotch,
|
|
|
|
borderAlpha,
|
|
|
|
borderColor,
|
|
|
|
borderThickness
|
|
|
|
})
|
|
|
|
|
|
|
|
this.flagOptions = Object.assign(
|
|
|
|
{
|
|
|
|
notchSize: 10,
|
|
|
|
notchWidth: null,
|
|
|
|
notchHeight: null,
|
|
|
|
padding: { x: 0, y: 0 },
|
|
|
|
originOffset: { x: 0, y: 0 }
|
|
|
|
},
|
|
|
|
opts.flag
|
|
|
|
)
|
|
|
|
|
|
|
|
this.border = this.borderThickness > 0
|
|
|
|
|
|
|
|
Object.assign(this.flagOptions, {
|
|
|
|
width: this.text.width + this.flagOptions.padding.x * 2,
|
|
|
|
height: this.text.height + this.flagOptions.padding.y * 2
|
|
|
|
})
|
|
|
|
|
|
|
|
this.flagPolygon = new FlagPolygon(this.flagOptions)
|
|
|
|
|
|
|
|
// hover effect
|
|
|
|
//--------------------
|
|
|
|
const duration = 0.2
|
|
|
|
this.graphics.interactive = true
|
|
|
|
this.graphics.on('pointerover', event => {
|
|
|
|
TweenLite.to(this.graphics, duration, { alpha: 0.6 })
|
|
|
|
})
|
|
|
|
this.graphics.on('pointerout', event => {
|
|
|
|
TweenLite.to(this.graphics, duration, { alpha: 1 })
|
|
|
|
})
|
|
|
|
this.graphics.on('pointerupoutside', event => {
|
|
|
|
TweenLite.to(this.graphics, duration, { alpha: 1 })
|
|
|
|
})
|
|
|
|
this.graphics.on('pointercancel', event => {
|
|
|
|
TweenLite.to(this.graphics, duration, { alpha: 1 })
|
|
|
|
})
|
|
|
|
this.graphics.on('pointertap', event => {
|
|
|
|
TweenLite.to(this.graphics, duration, { alpha: 1 })
|
|
|
|
})
|
|
|
|
// this.graphics.on('pointerdown', event => {
|
|
|
|
// TweenLite.to(this.graphics, duration, { alpha: 0.45 })
|
|
|
|
// })
|
|
|
|
// this.graphics.on('pointerup', event => {
|
|
|
|
// TweenLite.to(this.graphics, duration, { alpha: 0.6 })
|
|
|
|
// })
|
|
|
|
}
|
|
|
|
|
|
|
|
_draw() {
|
|
|
|
super._draw()
|
|
|
|
|
|
|
|
this.graphics.beginFill(this.color, this.alpha)
|
|
|
|
this.graphics.drawPolygon(this.flagPolygon)
|
|
|
|
this.flagPolygon.placeText(this.text, this.flagOptions.padding)
|
|
|
|
|
|
|
|
if (this.border) {
|
|
|
|
const lineStyle = [this.borderThickness, this.borderColor, this.borderAlpha]
|
|
|
|
if (this.fillNotch) {
|
|
|
|
this.graphics.beginFill(this.borderColor)
|
|
|
|
this.graphics.drawPolygon(this.flagPolygon.notch)
|
|
|
|
this.graphics.endFill()
|
|
|
|
|
|
|
|
this.graphics.lineStyle(...lineStyle)
|
|
|
|
this.graphics.drawPolygon(this.flagPolygon.rect)
|
|
|
|
this.graphics.drawPolygon(this.flagPolygon.notch)
|
|
|
|
} else {
|
|
|
|
this.graphics.lineStyle(...lineStyle)
|
|
|
|
this.graphics.drawPolygon(this.flagPolygon)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
/**
|
|
|
|
* The GeoMultiShape displays multiple forms.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @class GeoMultiShape
|
|
|
|
* @extends {GeoShape}
|
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
export class GeoMultiShape extends GeoShape {
|
|
|
|
static _manipulatePoints(points, func) {
|
|
|
|
points.forEach(shape => {
|
|
|
|
GeoShape._manipulatePoints(shape, func)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
static _getPointsFrom(multiShapeArray) {
|
|
|
|
let points = []
|
|
|
|
multiShapeArray.forEach(shape => {
|
|
|
|
points = points.concat(GeoShape._getPointsFrom(shape))
|
|
|
|
})
|
|
|
|
return points
|
|
|
|
}
|
|
|
|
|
|
|
|
static _adaptPoint(coordinates, map) {
|
|
|
|
let points = []
|
|
|
|
coordinates.forEach(shape => {
|
|
|
|
let adaptedPoint = GeoShape._adaptPoint(shape, map)
|
|
|
|
points.push(adaptedPoint)
|
|
|
|
})
|
|
|
|
return points
|
|
|
|
}
|
|
|
|
|
|
|
|
calculateLocation() {
|
|
|
|
let coms = []
|
|
|
|
this.coordinates.forEach(polygon => {
|
|
|
|
coms.push(GeoGraphics.calculateCenterOfMass(polygon))
|
|
|
|
})
|
|
|
|
|
|
|
|
return GeoGraphics.calculateCenterOfMass(coms)
|
|
|
|
}
|
|
|
|
|
|
|
|
_drawFrom(multiShape) {
|
|
|
|
multiShape.forEach(shape => {
|
|
|
|
super._drawFrom(shape)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|