import { extend } from '../core/util'; import { getAlignPoint, escapeSpecialChars } from '../core/util/strings'; import Coordinate from '../geo/Coordinate'; import Size from '../geo/Size'; import { TextSymbol, VectorMarkerSymbol } from '../symbol'; import TextMarker, { TextMarkerOptionsType } from './TextMarker'; /** * @property {Object} [options=null] - textbox's options, also including options of [Marker]{@link Marker#options} * @property {Boolean} [options.boxStyle=null] - the default box style of text * @property {Boolean} [options.boxStyle.padding=[12, 8]] - text padding in the box * @property {Boolean} [options.boxStyle.verticalAlignment=middle] - text's vertical alignment * @property {Boolean} [options.boxStyle.horizontalAlignment=true] - text's horizontal alignment * @property {Number} [options.boxStyle.minWidth=0] - label box's minWidth * @property {Number} [options.boxStyle.minHeight=0] - label box's minHeight * @property {Boolean} [options.textSymbol=null] - text symbol of label * @memberOf Label * @instance */ const options: LabelOptionsType = { 'boxStyle': null, /*{ 'padding' : [12, 8], 'verticalAlignment' : 'middle', 'horizontalAlignment' : 'middle', 'minWidth' : 0, 'minHeight' : 0, 'symbol' : null }*/ textSymbol: null }; /** * @classdesc * Represents point type geometry for text labels.
* A label is used to draw text (with a box background if specified) on a particular coordinate. * @category geometry * @extends TextMarker * @mixes TextEditable * @example * var label = new maptalks.Label('label with a box', [0, 0], { 'draggable' : true, 'boxStyle' : { 'padding' : [12, 8], 'verticalAlignment' : 'top', 'horizontalAlignment' : 'right', 'minWidth' : 300, 'minHeight' : 200, 'symbol' : { 'markerType' : 'square', 'markerFill' : 'rgb(135,196,240)', 'markerFillOpacity' : 0.9, 'markerLineColor' : '#34495e', 'markerLineWidth' : 1 } }, 'textSymbol': { 'textFaceName' : 'monospace', 'textFill' : '#34495e', 'textHaloFill' : '#fff', 'textHaloRadius' : 4, 'textSize' : 18, 'textWeight' : 'bold', 'textVerticalAlignment' : 'top' } }); */ class Label extends TextMarker { options: any /** * @param {String} content - Label's text content * @param {Coordinate} coordinates - coordinates * @param {Object} [options=null] - construct options defined in [Label]{@link Label#options} */ constructor(content: string, coordinates: Coordinate | Array, options: LabelOptionsType = {}) { super(coordinates, options); this._content = escapeSpecialChars(content); if (options.textSymbol) { this.setTextSymbol(options.textSymbol); } if (options.boxStyle) { this.setBoxStyle(options.boxStyle); } this._refresh(); } /** * 获取标注的边框样式 * @english * Get label's box style * @return {Object} */ getBoxStyle(): BoxStyle { if (!this.options.boxStyle) { return null; } return extend({}, this.options.boxStyle); } /** * 设置标注的边框样式 * @english * Set a new box style to the label * @param {Object} * @returns {Label} this */ setBoxStyle(style: BoxStyle) { this.options.boxStyle = style ? extend({}, style) : style; this._refresh(); return this; } /** * 获取标注的文本样式 * Get label's text symbol * @return {Object} */ getTextSymbol(): TextSymbol { return extend({}, this._getDefaultTextSymbol(), this.options.textSymbol); } /** * 给标注设置新的文本样式 * @english * Set a new text symbol to the label * @param {Object} symbol * @returns {Label} this */ setTextSymbol(symbol: TextSymbol) { this.options.textSymbol = symbol ? extend({}, symbol) : symbol; this._refresh(); return this; } static fromJSON(json: { [key: string]: any }): Label { const feature = json['feature']; const label = new Label(json['content'], feature['geometry']['coordinates'], json['options']); label.setProperties(feature['properties']); label.setId(feature['id']); if (json['symbol']) { label.setSymbol(json['symbol']); } return label; } //@internal _canEdit(): boolean { return false; } //@internal _toJSON(options: any) { return { 'feature': this.toGeoJSON(options), 'subType': 'Label', 'content': this._content }; } //@internal _refresh(): void { const symbol = extend({}, this.getTextSymbol(), { 'textName': this._content }); const boxStyle = this.getBoxStyle(); if (boxStyle) { extend(symbol, boxStyle.symbol); const sizes = this._getBoxSize(symbol), textSize = sizes[1], padding = boxStyle['padding'] || this._getDefaultPadding(); const boxSize = sizes[0]; //if no boxSize then use text's size in default symbol['markerWidth'] = boxSize['width']; symbol['markerHeight'] = boxSize['height']; const dx = symbol['textDx'] || 0, dy = symbol['textDy'] || 0, textAlignPoint = getAlignPoint(textSize, symbol['textHorizontalAlignment'], symbol['textVerticalAlignment']) ._add(dx as number, dy as number); const hAlign = boxStyle['horizontalAlignment'] || 'middle'; symbol['markerDx'] = textAlignPoint.x; if (hAlign === 'left') { symbol['markerDx'] += symbol['markerWidth'] / 2 - padding[0]; } else if (hAlign === 'right') { symbol['markerDx'] -= symbol['markerWidth'] / 2 - textSize['width'] - padding[0]; } else { symbol['markerDx'] += textSize['width'] / 2; } const vAlign = boxStyle['verticalAlignment'] || 'middle'; symbol['markerDy'] = textAlignPoint.y; if (vAlign === 'top') { symbol['markerDy'] += symbol['markerHeight'] / 2 - padding[1]; } else if (vAlign === 'bottom') { symbol['markerDy'] -= symbol['markerHeight'] / 2 - textSize['height'] - padding[1]; } else { symbol['markerDy'] += textSize['height'] / 2; } } this._refreshing = true; this.updateSymbol(symbol); delete this._refreshing; } //@internal _getBoxSize(symbol: any): any { if (!symbol['markerType']) { symbol['markerType'] = 'square'; } const boxStyle = this.getBoxStyle(); const size = this._getTextSize(symbol); let width, height; const padding = boxStyle['padding'] || this._getDefaultPadding(); width = size['width'] + padding[0] * 2; height = size['height'] + padding[1] * 2; if (boxStyle['minWidth']) { if (!width || width < boxStyle['minWidth']) { width = boxStyle['minWidth']; } } if (boxStyle['minHeight']) { if (!height || height < boxStyle['minHeight']) { height = boxStyle['minHeight']; } } return [new Size(width, height), size]; } } Label.mergeOptions(options); Label.registerJSONType('Label'); export default Label; type BoxStyle = { padding?: [number, number]; verticalAlignment?: 'top' | 'middle' | 'bottom'; horizontalAlignment?: 'left' | 'middle' | 'right'; minWidth?: number; minHeight?: number; symbol?: VectorMarkerSymbol; } export type LabelOptionsType = TextMarkerOptionsType & { textSymbol?: TextSymbol; boxStyle?: BoxStyle; };