Carga
Carga
This commit is contained in:
315
static/lib/Highcharts-10.2.1/es-modules/Maps/MapNavigation.js
Normal file
315
static/lib/Highcharts-10.2.1/es-modules/Maps/MapNavigation.js
Normal file
@@ -0,0 +1,315 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Chart from '../Core/Chart/Chart.js';
|
||||
import H from '../Core/Globals.js';
|
||||
var doc = H.doc;
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, extend = U.extend, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick;
|
||||
import './MapNavigationOptionsDefault.js';
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function stopEvent(e) {
|
||||
if (e) {
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
e.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The MapNavigation handles buttons for navigation in addition to mousewheel
|
||||
* and doubleclick handlers for chart zooming.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name MapNavigation
|
||||
*
|
||||
* @param {Highcharts.Chart} chart
|
||||
* The Chart instance.
|
||||
*/
|
||||
function MapNavigation(chart) {
|
||||
this.navButtons = [];
|
||||
this.init(chart);
|
||||
}
|
||||
/**
|
||||
* Initialize function.
|
||||
*
|
||||
* @function MapNavigation#init
|
||||
*
|
||||
* @param {Highcharts.Chart} chart
|
||||
* The Chart instance.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
MapNavigation.prototype.init = function (chart) {
|
||||
this.chart = chart;
|
||||
};
|
||||
/**
|
||||
* Update the map navigation with new options. Calling this is the same as
|
||||
* calling `chart.update({ mapNavigation: {} })`.
|
||||
*
|
||||
* @function MapNavigation#update
|
||||
*
|
||||
* @param {Highcharts.MapNavigationOptions} [options]
|
||||
* New options for the map navigation.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
MapNavigation.prototype.update = function (options) {
|
||||
var mapNav = this, chart = this.chart, o = chart.options.mapNavigation, attr, outerHandler = function (e) {
|
||||
this.handler.call(chart, e);
|
||||
stopEvent(e); // Stop default click event (#4444)
|
||||
}, navButtons = mapNav.navButtons;
|
||||
// Merge in new options in case of update, and register back to chart
|
||||
// options.
|
||||
if (options) {
|
||||
o = chart.options.mapNavigation =
|
||||
merge(chart.options.mapNavigation, options);
|
||||
}
|
||||
// Destroy buttons in case of dynamic update
|
||||
while (navButtons.length) {
|
||||
navButtons.pop().destroy();
|
||||
}
|
||||
if (pick(o.enableButtons, o.enabled) && !chart.renderer.forExport) {
|
||||
if (!mapNav.navButtonsGroup) {
|
||||
mapNav.navButtonsGroup = chart.renderer.g().attr({
|
||||
zIndex: 4 // #4955, // #8392
|
||||
}).add();
|
||||
}
|
||||
objectEach(o.buttons, function (buttonOptions, n) {
|
||||
buttonOptions = merge(o.buttonOptions, buttonOptions);
|
||||
// Presentational
|
||||
if (!chart.styledMode && buttonOptions.theme) {
|
||||
attr = buttonOptions.theme;
|
||||
attr.style = merge(buttonOptions.theme.style, buttonOptions.style // #3203
|
||||
);
|
||||
}
|
||||
var button = chart.renderer
|
||||
.button(buttonOptions.text || '', 0, 0, outerHandler, attr, void 0, void 0, void 0, n === 'zoomIn' ? 'topbutton' : 'bottombutton')
|
||||
.addClass('highcharts-map-navigation highcharts-' + {
|
||||
zoomIn: 'zoom-in',
|
||||
zoomOut: 'zoom-out'
|
||||
}[n])
|
||||
.attr({
|
||||
width: buttonOptions.width,
|
||||
height: buttonOptions.height,
|
||||
title: chart.options.lang[n],
|
||||
padding: buttonOptions.padding,
|
||||
zIndex: 5
|
||||
})
|
||||
.add(mapNav.navButtonsGroup);
|
||||
button.handler = buttonOptions.onclick;
|
||||
// Stop double click event (#4444)
|
||||
addEvent(button.element, 'dblclick', stopEvent);
|
||||
navButtons.push(button);
|
||||
extend(buttonOptions, {
|
||||
width: button.width,
|
||||
height: 2 * button.height
|
||||
});
|
||||
if (!chart.hasLoaded) {
|
||||
// Align it after the plotBox is known (#12776)
|
||||
var unbind_1 = addEvent(chart, 'load', function () {
|
||||
// #15406: Make sure button hasnt been destroyed
|
||||
if (button.element) {
|
||||
button.align(buttonOptions, false, buttonOptions.alignTo);
|
||||
}
|
||||
unbind_1();
|
||||
});
|
||||
}
|
||||
else {
|
||||
button.align(buttonOptions, false, buttonOptions.alignTo);
|
||||
}
|
||||
});
|
||||
// Borrowed from overlapping-datalabels. Consider a shared module.
|
||||
var isIntersectRect_1 = function (box1, box2) { return !(box2.x >= box1.x + box1.width ||
|
||||
box2.x + box2.width <= box1.x ||
|
||||
box2.y >= box1.y + box1.height ||
|
||||
box2.y + box2.height <= box1.y); };
|
||||
// Check the mapNavigation buttons collision with exporting button
|
||||
// and translate the mapNavigation button if they overlap.
|
||||
var adjustMapNavBtn = function () {
|
||||
var expBtnBBox = chart.exportingGroup && chart.exportingGroup.getBBox();
|
||||
if (expBtnBBox) {
|
||||
var navBtnsBBox = mapNav.navButtonsGroup.getBBox();
|
||||
// If buttons overlap
|
||||
if (isIntersectRect_1(expBtnBBox, navBtnsBBox)) {
|
||||
// Adjust the mapNav buttons' position by translating them
|
||||
// above or below the exporting button
|
||||
var aboveExpBtn = -navBtnsBBox.y - navBtnsBBox.height +
|
||||
expBtnBBox.y - 5, belowExpBtn = expBtnBBox.y + expBtnBBox.height -
|
||||
navBtnsBBox.y + 5, mapNavVerticalAlign = o.buttonOptions && o.buttonOptions.verticalAlign;
|
||||
// If bottom aligned and adjusting the mapNav button would
|
||||
// translate it out of the plotBox, translate it up
|
||||
// instead of down
|
||||
mapNav.navButtonsGroup.attr({
|
||||
translateY: mapNavVerticalAlign === 'bottom' ?
|
||||
aboveExpBtn :
|
||||
belowExpBtn
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!chart.hasLoaded) {
|
||||
// Align it after the plotBox is known (#12776) and after the
|
||||
// hamburger button's position is known so they don't overlap
|
||||
// (#15782)
|
||||
addEvent(chart, 'render', adjustMapNavBtn);
|
||||
}
|
||||
}
|
||||
this.updateEvents(o);
|
||||
};
|
||||
/**
|
||||
* Update events, called internally from the update function. Add new event
|
||||
* handlers, or unbinds events if disabled.
|
||||
*
|
||||
* @function MapNavigation#updateEvents
|
||||
*
|
||||
* @param {Highcharts.MapNavigationOptions} options
|
||||
* Options for map navigation.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
MapNavigation.prototype.updateEvents = function (options) {
|
||||
var chart = this.chart;
|
||||
// Add the double click event
|
||||
if (pick(options.enableDoubleClickZoom, options.enabled) ||
|
||||
options.enableDoubleClickZoomTo) {
|
||||
this.unbindDblClick = this.unbindDblClick || addEvent(chart.container, 'dblclick', function (e) {
|
||||
chart.pointer.onContainerDblClick(e);
|
||||
});
|
||||
}
|
||||
else if (this.unbindDblClick) {
|
||||
// Unbind and set unbinder to undefined
|
||||
this.unbindDblClick = this.unbindDblClick();
|
||||
}
|
||||
// Add the mousewheel event
|
||||
if (pick(options.enableMouseWheelZoom, options.enabled)) {
|
||||
this.unbindMouseWheel = this.unbindMouseWheel || addEvent(chart.container, doc.onwheel !== void 0 ? 'wheel' : // Newer Firefox
|
||||
doc.onmousewheel !== void 0 ? 'mousewheel' :
|
||||
'DOMMouseScroll', function (e) {
|
||||
// Prevent scrolling when the pointer is over the element with
|
||||
// that class, for example anotation popup #12100.
|
||||
if (!chart.pointer.inClass(e.target, 'highcharts-no-mousewheel')) {
|
||||
chart.pointer.onContainerMouseWheel(e);
|
||||
// Issue #5011, returning false from non-jQuery event does
|
||||
// not prevent default
|
||||
stopEvent(e);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else if (this.unbindMouseWheel) {
|
||||
// Unbind and set unbinder to undefined
|
||||
this.unbindMouseWheel = this.unbindMouseWheel();
|
||||
}
|
||||
};
|
||||
// Add events to the Chart object itself
|
||||
extend(Chart.prototype, /** @lends Chart.prototype */ {
|
||||
/**
|
||||
* Fit an inner box to an outer. If the inner box overflows left or right,
|
||||
* align it to the sides of the outer. If it overflows both sides, fit it
|
||||
* within the outer. This is a pattern that occurs more places in
|
||||
* Highcharts, perhaps it should be elevated to a common utility function.
|
||||
*
|
||||
* @ignore
|
||||
* @function Highcharts.Chart#fitToBox
|
||||
*
|
||||
* @param {Highcharts.BBoxObject} inner
|
||||
*
|
||||
* @param {Highcharts.BBoxObject} outer
|
||||
*
|
||||
* @return {Highcharts.BBoxObject}
|
||||
* The inner box
|
||||
*/
|
||||
fitToBox: function (inner, outer) {
|
||||
[['x', 'width'], ['y', 'height']].forEach(function (dim) {
|
||||
var pos = dim[0], size = dim[1];
|
||||
if (inner[pos] + inner[size] >
|
||||
outer[pos] + outer[size]) { // right
|
||||
// the general size is greater, fit fully to outer
|
||||
if (inner[size] > outer[size]) {
|
||||
inner[size] = outer[size];
|
||||
inner[pos] = outer[pos];
|
||||
}
|
||||
else { // align right
|
||||
inner[pos] = outer[pos] +
|
||||
outer[size] - inner[size];
|
||||
}
|
||||
}
|
||||
if (inner[size] > outer[size]) {
|
||||
inner[size] = outer[size];
|
||||
}
|
||||
if (inner[pos] < outer[pos]) {
|
||||
inner[pos] = outer[pos];
|
||||
}
|
||||
});
|
||||
return inner;
|
||||
},
|
||||
/**
|
||||
* Highcharts Maps only. Zoom in or out of the map. See also
|
||||
* {@link Point#zoomTo}. See {@link Chart#fromLatLonToPoint} for how to get
|
||||
* the `centerX` and `centerY` parameters for a geographic location.
|
||||
*
|
||||
* Deprecated as of v9.3 in favor of [MapView.zoomBy](https://api.highcharts.com/class-reference/Highcharts.MapView#zoomBy).
|
||||
*
|
||||
* @deprecated
|
||||
* @function Highcharts.Chart#mapZoom
|
||||
*
|
||||
* @param {number} [howMuch]
|
||||
* How much to zoom the map. Values less than 1 zooms in. 0.5 zooms
|
||||
* in to half the current view. 2 zooms to twice the current view. If
|
||||
* omitted, the zoom is reset.
|
||||
*
|
||||
* @param {number} [xProjected]
|
||||
* The projected x position to keep stationary when zooming, if
|
||||
* available space.
|
||||
*
|
||||
* @param {number} [yProjected]
|
||||
* The projected y position to keep stationary when zooming, if
|
||||
* available space.
|
||||
*
|
||||
* @param {number} [chartX]
|
||||
* Keep this chart position stationary if possible. This is used for
|
||||
* example in mousewheel events, where the area under the mouse
|
||||
* should be fixed as we zoom in.
|
||||
*
|
||||
* @param {number} [chartY]
|
||||
* Keep this chart position stationary if possible.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
mapZoom: function (howMuch, xProjected, yProjected, chartX, chartY) {
|
||||
if (this.mapView) {
|
||||
if (isNumber(howMuch)) {
|
||||
// Compliance, mapView.zoomBy uses different values
|
||||
howMuch = Math.log(howMuch) / Math.log(0.5);
|
||||
}
|
||||
this.mapView.zoomBy(howMuch, isNumber(xProjected) && isNumber(yProjected) ?
|
||||
this.mapView.projection.inverse([xProjected, yProjected]) :
|
||||
void 0, isNumber(chartX) && isNumber(chartY) ?
|
||||
[chartX, chartY] :
|
||||
void 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Extend the Chart.render method to add zooming and panning
|
||||
addEvent(Chart, 'beforeRender', function () {
|
||||
// Render the plus and minus buttons. Doing this before the shapes makes
|
||||
// getBBox much quicker, at least in Chrome.
|
||||
this.mapNavigation = new MapNavigation(this);
|
||||
this.mapNavigation.update();
|
||||
});
|
||||
H.MapNavigation = MapNavigation;
|
||||
@@ -0,0 +1,264 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import D from '../Core/DefaultOptions.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var extend = U.extend;
|
||||
/* *
|
||||
*
|
||||
* Constants
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* The `mapNavigation` option handles buttons for navigation in addition to
|
||||
* mousewheel and doubleclick handlers for map zooming.
|
||||
*
|
||||
* @product highmaps
|
||||
* @optionparent mapNavigation
|
||||
*/
|
||||
var defaultOptions = {
|
||||
/**
|
||||
* General options for the map navigation buttons. Individual options
|
||||
* can be given from the [mapNavigation.buttons](#mapNavigation.buttons)
|
||||
* option set.
|
||||
*
|
||||
* @sample {highmaps} maps/mapnavigation/button-theme/
|
||||
* Theming the navigation buttons
|
||||
*/
|
||||
buttonOptions: {
|
||||
/**
|
||||
* What box to align the buttons to. Possible values are `plotBox`
|
||||
* and `spacingBox`.
|
||||
*
|
||||
* @type {Highcharts.ButtonRelativeToValue}
|
||||
*/
|
||||
alignTo: 'plotBox',
|
||||
/**
|
||||
* The alignment of the navigation buttons.
|
||||
*
|
||||
* @type {Highcharts.AlignValue}
|
||||
*/
|
||||
align: 'left',
|
||||
/**
|
||||
* The vertical alignment of the buttons. Individual alignment can
|
||||
* be adjusted by each button's `y` offset.
|
||||
*
|
||||
* @type {Highcharts.VerticalAlignValue}
|
||||
*/
|
||||
verticalAlign: 'top',
|
||||
/**
|
||||
* The X offset of the buttons relative to its `align` setting.
|
||||
*/
|
||||
x: 0,
|
||||
/**
|
||||
* The width of the map navigation buttons.
|
||||
*/
|
||||
width: 18,
|
||||
/**
|
||||
* The pixel height of the map navigation buttons.
|
||||
*/
|
||||
height: 18,
|
||||
/**
|
||||
* Padding for the navigation buttons.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
padding: 5,
|
||||
/**
|
||||
* Text styles for the map navigation buttons.
|
||||
*
|
||||
* @type {Highcharts.CSSObject}
|
||||
* @default {"fontSize": "15px", "fontWeight": "bold"}
|
||||
*/
|
||||
style: {
|
||||
/** @ignore */
|
||||
fontSize: '15px',
|
||||
/** @ignore */
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
/**
|
||||
* A configuration object for the button theme. The object accepts
|
||||
* SVG properties like `stroke-width`, `stroke` and `fill`. Tri-state
|
||||
* button styles are supported by the `states.hover` and `states.select`
|
||||
* objects.
|
||||
*
|
||||
* @sample {highmaps} maps/mapnavigation/button-theme/
|
||||
* Themed navigation buttons
|
||||
*
|
||||
* @type {Highcharts.SVGAttributes}
|
||||
* @default {"stroke-width": 1, "text-align": "center"}
|
||||
*/
|
||||
theme: {
|
||||
/** @ignore */
|
||||
'stroke-width': 1,
|
||||
/** @ignore */
|
||||
'text-align': 'center'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The individual buttons for the map navigation. This usually includes
|
||||
* the zoom in and zoom out buttons. Properties for each button is
|
||||
* inherited from
|
||||
* [mapNavigation.buttonOptions](#mapNavigation.buttonOptions), while
|
||||
* individual options can be overridden. But default, the `onclick`, `text`
|
||||
* and `y` options are individual.
|
||||
*/
|
||||
buttons: {
|
||||
/**
|
||||
* Options for the zoom in button. Properties for the zoom in and zoom
|
||||
* out buttons are inherited from
|
||||
* [mapNavigation.buttonOptions](#mapNavigation.buttonOptions), while
|
||||
* individual options can be overridden. By default, the `onclick`,
|
||||
* `text` and `y` options are individual.
|
||||
*
|
||||
* @extends mapNavigation.buttonOptions
|
||||
*/
|
||||
zoomIn: {
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Click handler for the button.
|
||||
*
|
||||
* @type {Function}
|
||||
* @default function () { this.mapZoom(0.5); }
|
||||
*/
|
||||
onclick: function () {
|
||||
this.mapZoom(0.5);
|
||||
},
|
||||
/**
|
||||
* The text for the button. The tooltip (title) is a language option
|
||||
* given by [lang.zoomIn](#lang.zoomIn).
|
||||
*/
|
||||
text: '+',
|
||||
/**
|
||||
* The position of the zoomIn button relative to the vertical
|
||||
* alignment.
|
||||
*/
|
||||
y: 0
|
||||
},
|
||||
/**
|
||||
* Options for the zoom out button. Properties for the zoom in and
|
||||
* zoom out buttons are inherited from
|
||||
* [mapNavigation.buttonOptions](#mapNavigation.buttonOptions), while
|
||||
* individual options can be overridden. By default, the `onclick`,
|
||||
* `text` and `y` options are individual.
|
||||
*
|
||||
* @extends mapNavigation.buttonOptions
|
||||
*/
|
||||
zoomOut: {
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Click handler for the button.
|
||||
*
|
||||
* @type {Function}
|
||||
* @default function () { this.mapZoom(2); }
|
||||
*/
|
||||
onclick: function () {
|
||||
this.mapZoom(2);
|
||||
},
|
||||
/**
|
||||
* The text for the button. The tooltip (title) is a language option
|
||||
* given by [lang.zoomOut](#lang.zoomIn).
|
||||
*/
|
||||
text: '-',
|
||||
/**
|
||||
* The position of the zoomOut button relative to the vertical
|
||||
* alignment.
|
||||
*/
|
||||
y: 28
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Whether to enable navigation buttons. By default it inherits the
|
||||
* [enabled](#mapNavigation.enabled) setting.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @apioption mapNavigation.enableButtons
|
||||
*/
|
||||
/**
|
||||
* Whether to enable map navigation. The default is not to enable
|
||||
* navigation, as many choropleth maps are simple and don't need it.
|
||||
* Additionally, when touch zoom and mousewheel zoom is enabled, it breaks
|
||||
* the default behaviour of these interactions in the website, and the
|
||||
* implementer should be aware of this.
|
||||
*
|
||||
* Individual interactions can be enabled separately, namely buttons,
|
||||
* multitouch zoom, double click zoom, double click zoom to element and
|
||||
* mousewheel zoom.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @apioption mapNavigation.enabled
|
||||
*/
|
||||
/**
|
||||
* Enables zooming in on an area on double clicking in the map. By default
|
||||
* it inherits the [enabled](#mapNavigation.enabled) setting.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @apioption mapNavigation.enableDoubleClickZoom
|
||||
*/
|
||||
/**
|
||||
* Whether to zoom in on an area when that area is double clicked.
|
||||
*
|
||||
* @sample {highmaps} maps/mapnavigation/doubleclickzoomto/
|
||||
* Enable double click zoom to
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @apioption mapNavigation.enableDoubleClickZoomTo
|
||||
*/
|
||||
/**
|
||||
* Enables zooming by mouse wheel. By default it inherits the [enabled](
|
||||
* #mapNavigation.enabled) setting.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @apioption mapNavigation.enableMouseWheelZoom
|
||||
*/
|
||||
/**
|
||||
* Whether to enable multitouch zooming. Note that if the chart covers the
|
||||
* viewport, this prevents the user from using multitouch and touchdrag on
|
||||
* the web page, so you should make sure the user is not trapped inside the
|
||||
* chart. By default it inherits the [enabled](#mapNavigation.enabled)
|
||||
* setting.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @apioption mapNavigation.enableTouchZoom
|
||||
*/
|
||||
/**
|
||||
* Sensitivity of mouse wheel or trackpad scrolling. 1 is no sensitivity,
|
||||
* while with 2, one mousewheel delta will zoom in 50%.
|
||||
*
|
||||
* @since 4.2.4
|
||||
*/
|
||||
mouseWheelSensitivity: 1.1
|
||||
// enabled: false,
|
||||
// enableButtons: null, // inherit from enabled
|
||||
// enableTouchZoom: null, // inherit from enabled
|
||||
// enableDoubleClickZoom: null, // inherit from enabled
|
||||
// enableDoubleClickZoomTo: false
|
||||
// enableMouseWheelZoom: null, // inherit from enabled
|
||||
};
|
||||
/* *
|
||||
*
|
||||
* Composition
|
||||
*
|
||||
* */
|
||||
// Add language
|
||||
extend(D.defaultOptions.lang, {
|
||||
zoomIn: 'Zoom in',
|
||||
zoomOut: 'Zoom out'
|
||||
});
|
||||
// Set the default map navigation options
|
||||
D.defaultOptions.mapNavigation = defaultOptions;
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default defaultOptions;
|
||||
99
static/lib/Highcharts-10.2.1/es-modules/Maps/MapPointer.js
Normal file
99
static/lib/Highcharts-10.2.1/es-modules/Maps/MapPointer.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Pointer from '../Core/Pointer.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var defined = U.defined, extend = U.extend, pick = U.pick, wrap = U.wrap;
|
||||
/* eslint-disable no-invalid-this */
|
||||
var normalize = Pointer.prototype.normalize;
|
||||
var totalWheelDelta = 0;
|
||||
var totalWheelDeltaTimer;
|
||||
// Extend the Pointer
|
||||
extend(Pointer.prototype, {
|
||||
// Add lon and lat information to pointer events
|
||||
normalize: function (e, chartPosition) {
|
||||
var chart = this.chart;
|
||||
e = normalize.call(this, e, chartPosition);
|
||||
if (chart && chart.mapView) {
|
||||
var lonLat = chart.mapView.pixelsToLonLat({
|
||||
x: e.chartX - chart.plotLeft,
|
||||
y: e.chartY - chart.plotTop
|
||||
});
|
||||
if (lonLat) {
|
||||
extend(e, lonLat);
|
||||
}
|
||||
}
|
||||
return e;
|
||||
},
|
||||
// The event handler for the doubleclick event
|
||||
onContainerDblClick: function (e) {
|
||||
var chart = this.chart;
|
||||
e = this.normalize(e);
|
||||
if (chart.options.mapNavigation.enableDoubleClickZoomTo) {
|
||||
if (chart.pointer.inClass(e.target, 'highcharts-tracker') &&
|
||||
chart.hoverPoint) {
|
||||
chart.hoverPoint.zoomTo();
|
||||
}
|
||||
}
|
||||
else if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
|
||||
chart.mapZoom(0.5, void 0, void 0, e.chartX, e.chartY);
|
||||
}
|
||||
},
|
||||
// The event handler for the mouse scroll event
|
||||
onContainerMouseWheel: function (e) {
|
||||
var chart = this.chart;
|
||||
e = this.normalize(e);
|
||||
// Firefox uses e.deltaY or e.detail, WebKit and IE uses wheelDelta
|
||||
// try wheelDelta first #15656
|
||||
var delta = (defined(e.wheelDelta) && -e.wheelDelta / 120) ||
|
||||
e.deltaY || e.detail;
|
||||
// Wheel zooming on trackpads have different behaviours in Firefox vs
|
||||
// WebKit. In Firefox the delta increments in steps by 1, so it is not
|
||||
// distinguishable from true mouse wheel. Therefore we use this timer
|
||||
// to avoid trackpad zooming going too fast and out of control. In
|
||||
// WebKit however, the delta is < 1, so we simply disable animation in
|
||||
// the `chart.mapZoom` call below.
|
||||
if (Math.abs(delta) >= 1) {
|
||||
totalWheelDelta += Math.abs(delta);
|
||||
if (totalWheelDeltaTimer) {
|
||||
clearTimeout(totalWheelDeltaTimer);
|
||||
}
|
||||
totalWheelDeltaTimer = setTimeout(function () {
|
||||
totalWheelDelta = 0;
|
||||
}, 50);
|
||||
}
|
||||
if (totalWheelDelta < 10 && chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop) && chart.mapView) {
|
||||
chart.mapView.zoomBy((chart.options.mapNavigation.mouseWheelSensitivity -
|
||||
1) * -delta, void 0, [e.chartX, e.chartY],
|
||||
// Delta less than 1 indicates stepless/trackpad zooming, avoid
|
||||
// animation delaying the zoom
|
||||
Math.abs(delta) < 1 ? false : void 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
// The pinchType is inferred from mapNavigation options.
|
||||
wrap(Pointer.prototype, 'zoomOption', function (proceed) {
|
||||
var mapNavigation = this.chart.options.mapNavigation;
|
||||
// Pinch status
|
||||
if (pick(mapNavigation.enableTouchZoom, mapNavigation.enabled)) {
|
||||
this.chart.options.chart.zooming.pinchType = 'xy';
|
||||
}
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
});
|
||||
// Extend the pinchTranslate method to preserve fixed ratio when zooming
|
||||
wrap(Pointer.prototype, 'pinchTranslate', function (proceed, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
|
||||
var xBigger;
|
||||
proceed.call(this, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
|
||||
// Keep ratio
|
||||
if (this.chart.options.chart.type === 'map' && this.hasZoom) {
|
||||
xBigger = transform.scaleX > transform.scaleY;
|
||||
this.pinchTranslateDirection(!xBigger, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, xBigger ? transform.scaleX : transform.scaleY);
|
||||
}
|
||||
});
|
||||
80
static/lib/Highcharts-10.2.1/es-modules/Maps/MapSymbols.js
Normal file
80
static/lib/Highcharts-10.2.1/es-modules/Maps/MapSymbols.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import SVGRenderer from '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
var symbols = SVGRenderer.prototype.symbols;
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable require-jsdoc, valid-jsdoc */
|
||||
function bottomButton(x, y, w, h, options) {
|
||||
var r = (options && options.r) || 0;
|
||||
return selectiveRoundedRect(x - 1, y - 1, w, h, 0, 0, r, r);
|
||||
}
|
||||
/**
|
||||
* Create symbols for the zoom buttons
|
||||
* @private
|
||||
*/
|
||||
function selectiveRoundedRect(x, y, w, h, rTopLeft, rTopRight, rBottomRight, rBottomLeft) {
|
||||
return [
|
||||
['M', x + rTopLeft, y],
|
||||
// top side
|
||||
['L', x + w - rTopRight, y],
|
||||
// top right corner
|
||||
[
|
||||
'C',
|
||||
x + w - rTopRight / 2,
|
||||
y,
|
||||
x + w,
|
||||
y + rTopRight / 2,
|
||||
x + w,
|
||||
y + rTopRight
|
||||
],
|
||||
// right side
|
||||
['L', x + w, y + h - rBottomRight],
|
||||
// bottom right corner
|
||||
[
|
||||
'C', x + w, y + h - rBottomRight / 2,
|
||||
x + w - rBottomRight / 2, y + h,
|
||||
x + w - rBottomRight, y + h
|
||||
],
|
||||
// bottom side
|
||||
['L', x + rBottomLeft, y + h],
|
||||
// bottom left corner
|
||||
[
|
||||
'C',
|
||||
x + rBottomLeft / 2,
|
||||
y + h,
|
||||
x,
|
||||
y + h - rBottomLeft / 2,
|
||||
x,
|
||||
y + h - rBottomLeft
|
||||
],
|
||||
// left side
|
||||
['L', x, y + rTopLeft],
|
||||
// top left corner
|
||||
['C', x, y + rTopLeft / 2, x + rTopLeft / 2, y, x + rTopLeft, y],
|
||||
['Z']
|
||||
];
|
||||
}
|
||||
function topButton(x, y, w, h, options) {
|
||||
var r = (options && options.r) || 0;
|
||||
return selectiveRoundedRect(x - 1, y - 1, w, h, r, r, 0, 0);
|
||||
}
|
||||
symbols.bottombutton = bottomButton;
|
||||
symbols.topbutton = topButton;
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default symbols;
|
||||
56
static/lib/Highcharts-10.2.1/es-modules/Maps/MapUtilities.js
Normal file
56
static/lib/Highcharts-10.2.1/es-modules/Maps/MapUtilities.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
// Compute bounds from a path element
|
||||
var boundsFromPath = function (path) {
|
||||
var x2 = -Number.MAX_VALUE, x1 = Number.MAX_VALUE, y2 = -Number.MAX_VALUE, y1 = Number.MAX_VALUE, validBounds;
|
||||
path.forEach(function (seg) {
|
||||
var x = seg[seg.length - 2], y = seg[seg.length - 1];
|
||||
if (typeof x === 'number' &&
|
||||
typeof y === 'number') {
|
||||
x1 = Math.min(x1, x);
|
||||
x2 = Math.max(x2, x);
|
||||
y1 = Math.min(y1, y);
|
||||
y2 = Math.max(y2, y);
|
||||
validBounds = true;
|
||||
}
|
||||
});
|
||||
if (validBounds) {
|
||||
return { x1: x1, y1: y1, x2: x2, y2: y2 };
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Test for point in polygon. Polygon defined as array of [x,y] points.
|
||||
* @private
|
||||
*/
|
||||
var pointInPolygon = function (point, polygon) {
|
||||
var i, j, rel1, rel2, c = false, x = point.x, y = point.y;
|
||||
for (i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
rel1 = polygon[i][1] > y;
|
||||
rel2 = polygon[j][1] > y;
|
||||
if (rel1 !== rel2 &&
|
||||
(x < (polygon[j][0] - polygon[i][0]) * (y - polygon[i][1]) /
|
||||
(polygon[j][1] - polygon[i][1]) +
|
||||
polygon[i][0])) {
|
||||
c = !c;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
};
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
var MapUtilities = {
|
||||
boundsFromPath: boundsFromPath,
|
||||
pointInPolygon: pointInPolygon
|
||||
};
|
||||
export default MapUtilities;
|
||||
933
static/lib/Highcharts-10.2.1/es-modules/Maps/MapView.js
Normal file
933
static/lib/Highcharts-10.2.1/es-modules/Maps/MapView.js
Normal file
@@ -0,0 +1,933 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
if (typeof b !== "function" && b !== null)
|
||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
||||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
||||
if (ar || !(i in from)) {
|
||||
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
||||
ar[i] = from[i];
|
||||
}
|
||||
}
|
||||
return to.concat(ar || Array.prototype.slice.call(from));
|
||||
};
|
||||
import defaultOptions from './MapViewOptionsDefault.js';
|
||||
import defaultInsetsOptions from './MapViewInsetsOptionsDefault.js';
|
||||
import GeoJSONModule from '../Extensions/GeoJSON.js';
|
||||
var topo2geo = GeoJSONModule.topo2geo;
|
||||
import MapChart from '../Core/Chart/MapChart.js';
|
||||
var maps = MapChart.maps;
|
||||
import MU from './MapUtilities.js';
|
||||
var boundsFromPath = MU.boundsFromPath, pointInPolygon = MU.pointInPolygon;
|
||||
import Projection from './Projection.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, clamp = U.clamp, fireEvent = U.fireEvent, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, merge = U.merge, pick = U.pick, relativeLength = U.relativeLength;
|
||||
/**
|
||||
* The world size in terms of 10k meters in the Web Mercator projection, to
|
||||
* match a 256 square tile to zoom level 0.
|
||||
* @private
|
||||
*/
|
||||
var worldSize = 400.979322;
|
||||
var tileSize = 256;
|
||||
// Compute the zoom from given bounds and the size of the playing field. Used in
|
||||
// two places, hence the local function.
|
||||
var zoomFromBounds = function (b, playingField) {
|
||||
var width = playingField.width, height = playingField.height, scaleToField = Math.max((b.x2 - b.x1) / (width / tileSize), (b.y2 - b.y1) / (height / tileSize));
|
||||
return Math.log(worldSize / scaleToField) / Math.log(2);
|
||||
};
|
||||
/*
|
||||
const mergeCollections = <
|
||||
T extends Array<AnyRecord|undefined>
|
||||
>(a: T, b: T): T => {
|
||||
b.forEach((newer, i): void => {
|
||||
// Only merge by id supported for now. We may consider later to support
|
||||
// more complex rules like those of `Chart.update` with `oneToOne`, but
|
||||
// it is probably not needed. Existing insets can be disabled by
|
||||
// overwriting the `geoBounds` with empty data.
|
||||
if (newer && isString(newer.id)) {
|
||||
const older = U.find(
|
||||
a,
|
||||
(aItem): boolean => (aItem && aItem.id) === newer.id
|
||||
);
|
||||
if (older) {
|
||||
const aIndex = a.indexOf(older);
|
||||
a[aIndex] = merge(older, newer);
|
||||
}
|
||||
}
|
||||
});
|
||||
return a;
|
||||
};
|
||||
*/
|
||||
/**
|
||||
* The map view handles zooming and centering on the map, and various
|
||||
* client-side projection capabilities.
|
||||
*
|
||||
* On a chart instance, the map view is available as `chart.mapView`.
|
||||
*
|
||||
* @class
|
||||
* @name Highcharts.MapView
|
||||
*
|
||||
* @param {Highcharts.Chart} chart
|
||||
* The Chart instance
|
||||
* @param {Highcharts.MapViewOptions} options
|
||||
* MapView options
|
||||
*/
|
||||
var MapView = /** @class */ (function () {
|
||||
function MapView(chart, options) {
|
||||
var _this = this;
|
||||
this.insets = [];
|
||||
this.padding = [0, 0, 0, 0];
|
||||
this.eventsToUnbind = [];
|
||||
var recommendedMapView;
|
||||
var recommendedProjection;
|
||||
if (!(this instanceof MapViewInset)) {
|
||||
// Handle the global map and series-level mapData
|
||||
var geoMaps = __spreadArray([
|
||||
chart.options.chart.map
|
||||
], (chart.options.series || []).map(function (s) { return s.mapData; }), true).map(function (mapData) { return _this.getGeoMap(mapData); });
|
||||
var allGeoBounds_1 = [];
|
||||
geoMaps.forEach(function (geoMap) {
|
||||
if (geoMap) {
|
||||
// Use the first geo map as main
|
||||
if (!recommendedMapView) {
|
||||
recommendedMapView = geoMap['hc-recommended-mapview'];
|
||||
}
|
||||
// Combine the bounding boxes of all loaded maps
|
||||
if (geoMap.bbox) {
|
||||
var _a = geoMap.bbox, x1 = _a[0], y1 = _a[1], x2 = _a[2], y2 = _a[3];
|
||||
allGeoBounds_1.push({ x1: x1, y1: y1, x2: x2, y2: y2 });
|
||||
}
|
||||
}
|
||||
});
|
||||
// Get the composite bounds
|
||||
var geoBounds = (allGeoBounds_1.length &&
|
||||
MapView.compositeBounds(allGeoBounds_1));
|
||||
// Provide a best-guess recommended projection if not set in the map
|
||||
// or in user options
|
||||
if (geoBounds) {
|
||||
var x1 = geoBounds.x1, y1 = geoBounds.y1, x2 = geoBounds.x2, y2 = geoBounds.y2;
|
||||
recommendedProjection = (x2 - x1 > 180 && y2 - y1 > 90) ?
|
||||
// Wide angle, go for the world view
|
||||
{
|
||||
name: 'EqualEarth'
|
||||
} :
|
||||
// Narrower angle, use a projection better suited for local
|
||||
// view
|
||||
{
|
||||
name: 'LambertConformalConic',
|
||||
parallels: [y1, y2],
|
||||
rotation: [-(x1 + x2) / 2]
|
||||
};
|
||||
}
|
||||
// Register the main geo map (from options.chart.map) if set
|
||||
this.geoMap = geoMaps[0];
|
||||
}
|
||||
this.userOptions = options || {};
|
||||
var o = merge(defaultOptions, { projection: recommendedProjection }, recommendedMapView, options);
|
||||
// Merge the inset collections by id, or index if id missing
|
||||
var recInsets = recommendedMapView && recommendedMapView.insets, optInsets = options && options.insets;
|
||||
if (recInsets && optInsets) {
|
||||
o.insets = MapView.mergeInsets(recInsets, optInsets);
|
||||
}
|
||||
this.chart = chart;
|
||||
/**
|
||||
* The current center of the view in terms of `[longitude, latitude]`.
|
||||
* @name Highcharts.MapView#center
|
||||
* @readonly
|
||||
* @type {LonLatArray}
|
||||
*/
|
||||
this.center = o.center;
|
||||
this.options = o;
|
||||
this.projection = new Projection(o.projection);
|
||||
// Initialize with full plot box so we don't have to check for undefined
|
||||
// every time we use it
|
||||
this.playingField = chart.plotBox;
|
||||
/**
|
||||
* The current zoom level of the view.
|
||||
* @name Highcharts.MapView#zoom
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
this.zoom = o.zoom || 0;
|
||||
// Create the insets
|
||||
this.createInsets();
|
||||
// Initialize and respond to chart size changes
|
||||
this.eventsToUnbind.push(addEvent(chart, 'afterSetChartSize', function () {
|
||||
_this.playingField = _this.getField();
|
||||
if (_this.minZoom === void 0 || // When initializing the chart
|
||||
_this.minZoom === _this.zoom // When resizing the chart
|
||||
) {
|
||||
_this.fitToBounds(void 0, void 0, false);
|
||||
if (
|
||||
// Set zoom only when initializing the chart
|
||||
// (do not overwrite when zooming in/out, #17082)
|
||||
!_this.chart.hasRendered &&
|
||||
isNumber(_this.userOptions.zoom)) {
|
||||
_this.zoom = _this.userOptions.zoom;
|
||||
}
|
||||
if (_this.userOptions.center) {
|
||||
merge(true, _this.center, _this.userOptions.center);
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.setUpEvents();
|
||||
}
|
||||
// Merge two collections of insets by the id
|
||||
MapView.mergeInsets = function (a, b) {
|
||||
var toObject = function (insets) {
|
||||
var ob = {};
|
||||
insets.forEach(function (inset, i) {
|
||||
ob[inset && inset.id || "i".concat(i)] = inset;
|
||||
});
|
||||
return ob;
|
||||
};
|
||||
var insetsObj = merge(toObject(a), toObject(b)), insets = Object
|
||||
.keys(insetsObj)
|
||||
.map(function (key) { return insetsObj[key]; });
|
||||
return insets;
|
||||
};
|
||||
// Create MapViewInset instances from insets options
|
||||
MapView.prototype.createInsets = function () {
|
||||
var _this = this;
|
||||
var options = this.options, insets = options.insets;
|
||||
if (insets) {
|
||||
insets.forEach(function (item) {
|
||||
var inset = new MapViewInset(_this, merge(options.insetOptions, item));
|
||||
_this.insets.push(inset);
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Fit the view to given bounds
|
||||
*
|
||||
* @function Highcharts.MapView#fitToBounds
|
||||
* @param {Object} bounds
|
||||
* Bounds in terms of projected units given as `{ x1, y1, x2, y2 }`.
|
||||
* If not set, fit to the bounds of the current data set
|
||||
* @param {number|string} [padding=0]
|
||||
* Padding inside the bounds. A number signifies pixels, while a
|
||||
* percentage string (like `5%`) can be used as a fraction of the
|
||||
* plot area size.
|
||||
* @param {boolean} [redraw=true]
|
||||
* Whether to redraw the chart immediately
|
||||
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
|
||||
* What animation to use for redraw
|
||||
*/
|
||||
MapView.prototype.fitToBounds = function (bounds, padding, redraw, animation) {
|
||||
if (redraw === void 0) { redraw = true; }
|
||||
var b = bounds || this.getProjectedBounds();
|
||||
if (b) {
|
||||
var pad = pick(padding, bounds ? 0 : this.options.padding), fullField = this.getField(false), padArr = isArray(pad) ? pad : [pad, pad, pad, pad];
|
||||
this.padding = [
|
||||
relativeLength(padArr[0], fullField.height),
|
||||
relativeLength(padArr[1], fullField.width),
|
||||
relativeLength(padArr[2], fullField.height),
|
||||
relativeLength(padArr[3], fullField.width)
|
||||
];
|
||||
// Apply the playing field, corrected with padding
|
||||
this.playingField = this.getField();
|
||||
var zoom = zoomFromBounds(b, this.playingField);
|
||||
// Reset minZoom when fitting to natural bounds
|
||||
if (!bounds) {
|
||||
this.minZoom = zoom;
|
||||
}
|
||||
var center = this.projection.inverse([
|
||||
(b.x2 + b.x1) / 2,
|
||||
(b.y2 + b.y1) / 2
|
||||
]);
|
||||
this.setView(center, zoom, redraw, animation);
|
||||
}
|
||||
};
|
||||
MapView.prototype.getField = function (padded) {
|
||||
if (padded === void 0) { padded = true; }
|
||||
var padding = padded ? this.padding : [0, 0, 0, 0];
|
||||
return {
|
||||
x: padding[3],
|
||||
y: padding[0],
|
||||
width: this.chart.plotWidth - padding[1] - padding[3],
|
||||
height: this.chart.plotHeight - padding[0] - padding[2]
|
||||
};
|
||||
};
|
||||
MapView.prototype.getGeoMap = function (map) {
|
||||
if (isString(map)) {
|
||||
return maps[map];
|
||||
}
|
||||
if (isObject(map, true)) {
|
||||
if (map.type === 'FeatureCollection') {
|
||||
return map;
|
||||
}
|
||||
if (map.type === 'Topology') {
|
||||
return topo2geo(map);
|
||||
}
|
||||
}
|
||||
};
|
||||
MapView.prototype.getMapBBox = function () {
|
||||
var bounds = this.getProjectedBounds(), scale = this.getScale();
|
||||
if (bounds) {
|
||||
var padding = this.padding, p1 = this.projectedUnitsToPixels({
|
||||
x: bounds.x1,
|
||||
y: bounds.y2
|
||||
}), width = ((bounds.x2 - bounds.x1) * scale +
|
||||
padding[1] + padding[3]), height = ((bounds.y2 - bounds.y1) * scale +
|
||||
padding[0] + padding[2]);
|
||||
return {
|
||||
width: width,
|
||||
height: height,
|
||||
x: p1.x - padding[3],
|
||||
y: p1.y - padding[0]
|
||||
};
|
||||
}
|
||||
};
|
||||
MapView.prototype.getProjectedBounds = function () {
|
||||
var allBounds = this.chart.series.reduce(function (acc, s) {
|
||||
var bounds = s.getProjectedBounds && s.getProjectedBounds();
|
||||
if (bounds &&
|
||||
s.options.affectsMapView !== false) {
|
||||
acc.push(bounds);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
return this.projection.bounds || MapView.compositeBounds(allBounds);
|
||||
};
|
||||
MapView.prototype.getScale = function () {
|
||||
// A zoom of 0 means the world (360x360 degrees) fits in a 256x256 px
|
||||
// tile
|
||||
return (tileSize / worldSize) * Math.pow(2, this.zoom);
|
||||
};
|
||||
// Calculate the SVG transform to be applied to series groups
|
||||
MapView.prototype.getSVGTransform = function () {
|
||||
var _a = this.playingField, x = _a.x, y = _a.y, width = _a.width, height = _a.height, projectedCenter = this.projection.forward(this.center), flipFactor = this.projection.hasCoordinates ? -1 : 1, scaleX = this.getScale(), scaleY = scaleX * flipFactor, translateX = x + width / 2 - projectedCenter[0] * scaleX, translateY = y + height / 2 - projectedCenter[1] * scaleY;
|
||||
return { scaleX: scaleX, scaleY: scaleY, translateX: translateX, translateY: translateY };
|
||||
};
|
||||
/**
|
||||
* Convert map coordinates in longitude/latitude to pixels
|
||||
*
|
||||
* @function Highcharts.MapView#lonLatToPixels
|
||||
* @since 10.0.0
|
||||
* @param {Highcharts.MapLonLatObject} lonLat
|
||||
* The map coordinates
|
||||
* @return {Highcharts.PositionObject|undefined}
|
||||
* The pixel position
|
||||
*/
|
||||
MapView.prototype.lonLatToPixels = function (lonLat) {
|
||||
var pos = this.lonLatToProjectedUnits(lonLat);
|
||||
if (pos) {
|
||||
return this.projectedUnitsToPixels(pos);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Get projected units from longitude/latitude. Insets are accounted for.
|
||||
* Returns an object with x and y values corresponding to positions on the
|
||||
* projected plane.
|
||||
*
|
||||
* @requires modules/map
|
||||
*
|
||||
* @function Highcharts.MapView#lonLatToProjectedUnits
|
||||
*
|
||||
* @since 10.0.0
|
||||
* @sample maps/series/latlon-to-point/ Find a point from lon/lat
|
||||
*
|
||||
* @param {Highcharts.MapLonLatObject} lonLat Coordinates.
|
||||
*
|
||||
* @return {Highcharts.ProjectedXY} X and Y coordinates in terms of
|
||||
* projected values
|
||||
*/
|
||||
MapView.prototype.lonLatToProjectedUnits = function (lonLat) {
|
||||
var chart = this.chart, mapTransforms = chart.mapTransforms;
|
||||
// Legacy, built-in transforms
|
||||
if (mapTransforms) {
|
||||
for (var transform in mapTransforms) {
|
||||
if (Object.hasOwnProperty.call(mapTransforms, transform) &&
|
||||
mapTransforms[transform].hitZone) {
|
||||
var coords = chart.transformFromLatLon(lonLat, mapTransforms[transform]);
|
||||
if (coords && pointInPolygon(coords, mapTransforms[transform].hitZone.coordinates[0])) {
|
||||
return coords;
|
||||
}
|
||||
}
|
||||
}
|
||||
return chart.transformFromLatLon(lonLat, mapTransforms['default'] // eslint-disable-line dot-notation
|
||||
);
|
||||
}
|
||||
// Handle insets
|
||||
for (var _i = 0, _a = this.insets; _i < _a.length; _i++) {
|
||||
var inset = _a[_i];
|
||||
if (inset.options.geoBounds &&
|
||||
pointInPolygon({ x: lonLat.lon, y: lonLat.lat }, inset.options.geoBounds.coordinates[0])) {
|
||||
var insetProjectedPoint = inset.projection.forward([lonLat.lon, lonLat.lat]), pxPoint = inset.projectedUnitsToPixels({ x: insetProjectedPoint[0], y: insetProjectedPoint[1] });
|
||||
return this.pixelsToProjectedUnits(pxPoint);
|
||||
}
|
||||
}
|
||||
var point = this.projection.forward([lonLat.lon, lonLat.lat]);
|
||||
if (!point.outside) {
|
||||
return { x: point[0], y: point[1] };
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Calculate longitude/latitude values for a point or position. Returns an
|
||||
* object with the numeric properties `lon` and `lat`.
|
||||
*
|
||||
* @requires modules/map
|
||||
*
|
||||
* @function Highcharts.MapView#projectedUnitsToLonLat
|
||||
*
|
||||
* @since 10.0.0
|
||||
*
|
||||
* @sample maps/demo/latlon-advanced/ Advanced lat/lon demo
|
||||
*
|
||||
* @param {Highcharts.Point|Highcharts.ProjectedXY} point
|
||||
* A `Point` instance or anything containing `x` and `y` properties
|
||||
* with numeric values.
|
||||
*
|
||||
* @return {Highcharts.MapLonLatObject|undefined} An object with `lat` and
|
||||
* `lon` properties.
|
||||
*/
|
||||
MapView.prototype.projectedUnitsToLonLat = function (point) {
|
||||
var chart = this.chart, mapTransforms = chart.mapTransforms;
|
||||
// Legacy, built-in transforms
|
||||
if (mapTransforms) {
|
||||
for (var transform in mapTransforms) {
|
||||
if (Object.hasOwnProperty.call(mapTransforms, transform) &&
|
||||
mapTransforms[transform].hitZone &&
|
||||
pointInPolygon(point, mapTransforms[transform].hitZone.coordinates[0])) {
|
||||
return chart.transformToLatLon(point, mapTransforms[transform]);
|
||||
}
|
||||
}
|
||||
return chart.transformToLatLon(point, mapTransforms['default'] // eslint-disable-line dot-notation
|
||||
);
|
||||
}
|
||||
var pxPoint = this.projectedUnitsToPixels(point);
|
||||
for (var _i = 0, _a = this.insets; _i < _a.length; _i++) {
|
||||
var inset = _a[_i];
|
||||
if (inset.hitZone &&
|
||||
pointInPolygon(pxPoint, inset.hitZone.coordinates[0])) {
|
||||
var insetProjectedPoint = inset
|
||||
.pixelsToProjectedUnits(pxPoint), coordinates_1 = inset.projection.inverse([insetProjectedPoint.x, insetProjectedPoint.y]);
|
||||
return { lon: coordinates_1[0], lat: coordinates_1[1] };
|
||||
}
|
||||
}
|
||||
var coordinates = this.projection.inverse([point.x, point.y]);
|
||||
return { lon: coordinates[0], lat: coordinates[1] };
|
||||
};
|
||||
MapView.prototype.redraw = function (animation) {
|
||||
this.chart.series.forEach(function (s) {
|
||||
if (s.useMapGeometry) {
|
||||
s.isDirty = true;
|
||||
}
|
||||
});
|
||||
this.chart.redraw(animation);
|
||||
};
|
||||
/**
|
||||
* Set the view to given center and zoom values.
|
||||
* @function Highcharts.MapView#setView
|
||||
* @param {Highcharts.LonLatArray|undefined} center
|
||||
* The center point
|
||||
* @param {number} zoom
|
||||
* The zoom level
|
||||
* @param {boolean} [redraw=true]
|
||||
* Whether to redraw immediately
|
||||
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
|
||||
* Animation options for the redraw
|
||||
*
|
||||
* @sample maps/mapview/setview
|
||||
* Set the view programmatically
|
||||
*/
|
||||
MapView.prototype.setView = function (center, zoom, redraw, animation) {
|
||||
if (redraw === void 0) { redraw = true; }
|
||||
if (center) {
|
||||
this.center = center;
|
||||
}
|
||||
if (typeof zoom === 'number') {
|
||||
if (typeof this.minZoom === 'number') {
|
||||
zoom = Math.max(zoom, this.minZoom);
|
||||
}
|
||||
if (typeof this.options.maxZoom === 'number') {
|
||||
zoom = Math.min(zoom, this.options.maxZoom);
|
||||
}
|
||||
// Use isNumber to prevent Infinity (#17205)
|
||||
if (isNumber(zoom)) {
|
||||
this.zoom = zoom;
|
||||
}
|
||||
}
|
||||
var bounds = this.getProjectedBounds();
|
||||
if (bounds) {
|
||||
var projectedCenter = this.projection.forward(this.center), _a = this.playingField, x = _a.x, y = _a.y, width = _a.width, height = _a.height, scale = this.getScale(), bottomLeft = this.projectedUnitsToPixels({
|
||||
x: bounds.x1,
|
||||
y: bounds.y1
|
||||
}), topRight = this.projectedUnitsToPixels({
|
||||
x: bounds.x2,
|
||||
y: bounds.y2
|
||||
}), boundsCenterProjected = [
|
||||
(bounds.x1 + bounds.x2) / 2,
|
||||
(bounds.y1 + bounds.y2) / 2
|
||||
];
|
||||
// Constrain to data bounds
|
||||
// Pixel coordinate system is reversed vs projected
|
||||
var x1 = bottomLeft.x, y1 = topRight.y, x2 = topRight.x, y2 = bottomLeft.y;
|
||||
// Map smaller than plot area, center it
|
||||
if (x2 - x1 < width) {
|
||||
projectedCenter[0] = boundsCenterProjected[0];
|
||||
// Off west
|
||||
}
|
||||
else if (x1 < x && x2 < x + width) {
|
||||
// Adjust eastwards
|
||||
projectedCenter[0] += Math.max(x1 - x, x2 - width - x) / scale;
|
||||
// Off east
|
||||
}
|
||||
else if (x2 > x + width && x1 > x) {
|
||||
// Adjust westwards
|
||||
projectedCenter[0] += Math.min(x2 - width - x, x1 - x) / scale;
|
||||
}
|
||||
// Map smaller than plot area, center it
|
||||
if (y2 - y1 < height) {
|
||||
projectedCenter[1] = boundsCenterProjected[1];
|
||||
// Off north
|
||||
}
|
||||
else if (y1 < y && y2 < y + height) {
|
||||
// Adjust southwards
|
||||
projectedCenter[1] -= Math.max(y1 - y, y2 - height - y) / scale;
|
||||
// Off south
|
||||
}
|
||||
else if (y2 > y + height && y1 > y) {
|
||||
// Adjust northwards
|
||||
projectedCenter[1] -= Math.min(y2 - height - y, y1 - y) / scale;
|
||||
}
|
||||
this.center = this.projection.inverse(projectedCenter);
|
||||
this.insets.forEach(function (inset) {
|
||||
if (inset.options.field) {
|
||||
inset.hitZone = inset.getHitZone();
|
||||
inset.playingField = inset.getField();
|
||||
}
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
fireEvent(this, 'afterSetView');
|
||||
if (redraw) {
|
||||
this.redraw(animation);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Convert projected units to pixel position
|
||||
*
|
||||
* @function Highcharts.MapView#projectedUnitsToPixels
|
||||
* @param {Highcharts.PositionObject} pos
|
||||
* The position in projected units
|
||||
* @return {Highcharts.PositionObject} The position in pixels
|
||||
*/
|
||||
MapView.prototype.projectedUnitsToPixels = function (pos) {
|
||||
var scale = this.getScale(), projectedCenter = this.projection.forward(this.center), field = this.playingField, centerPxX = field.x + field.width / 2, centerPxY = field.y + field.height / 2;
|
||||
var x = centerPxX - scale * (projectedCenter[0] - pos.x);
|
||||
var y = centerPxY + scale * (projectedCenter[1] - pos.y);
|
||||
return { x: x, y: y };
|
||||
};
|
||||
/**
|
||||
* Convert pixel position to longitude and latitude.
|
||||
*
|
||||
* @function Highcharts.MapView#pixelsToLonLat
|
||||
* @since 10.0.0
|
||||
* @param {Highcharts.PositionObject} pos
|
||||
* The position in pixels
|
||||
* @return {Highcharts.MapLonLatObject|undefined}
|
||||
* The map coordinates
|
||||
*/
|
||||
MapView.prototype.pixelsToLonLat = function (pos) {
|
||||
return this.projectedUnitsToLonLat(this.pixelsToProjectedUnits(pos));
|
||||
};
|
||||
/**
|
||||
* Convert pixel position to projected units
|
||||
*
|
||||
* @function Highcharts.MapView#pixelsToProjectedUnits
|
||||
* @param {Highcharts.PositionObject} pos
|
||||
* The position in pixels
|
||||
* @return {Highcharts.PositionObject} The position in projected units
|
||||
*/
|
||||
MapView.prototype.pixelsToProjectedUnits = function (pos) {
|
||||
var x = pos.x, y = pos.y, scale = this.getScale(), projectedCenter = this.projection.forward(this.center), field = this.playingField, centerPxX = field.x + field.width / 2, centerPxY = field.y + field.height / 2;
|
||||
var projectedX = projectedCenter[0] + (x - centerPxX) / scale;
|
||||
var projectedY = projectedCenter[1] - (y - centerPxY) / scale;
|
||||
return { x: projectedX, y: projectedY };
|
||||
};
|
||||
MapView.prototype.setUpEvents = function () {
|
||||
var _this = this;
|
||||
var chart = this.chart;
|
||||
// Set up panning for maps. In orthographic projections the globe will
|
||||
// rotate, otherwise adjust the map center.
|
||||
var mouseDownCenterProjected;
|
||||
var mouseDownKey;
|
||||
var mouseDownRotation;
|
||||
var onPan = function (e) {
|
||||
var pinchDown = chart.pointer.pinchDown, projection = _this.projection;
|
||||
var mouseDownX = chart.mouseDownX, mouseDownY = chart.mouseDownY;
|
||||
if (pinchDown.length === 1) {
|
||||
mouseDownX = pinchDown[0].chartX;
|
||||
mouseDownY = pinchDown[0].chartY;
|
||||
}
|
||||
if (typeof mouseDownX === 'number' &&
|
||||
typeof mouseDownY === 'number') {
|
||||
var key = "".concat(mouseDownX, ",").concat(mouseDownY), _a = e.originalEvent, chartX = _a.chartX, chartY = _a.chartY;
|
||||
// Reset starting position
|
||||
if (key !== mouseDownKey) {
|
||||
mouseDownKey = key;
|
||||
mouseDownCenterProjected = _this.projection
|
||||
.forward(_this.center);
|
||||
mouseDownRotation = (_this.projection.options.rotation || [0, 0]).slice();
|
||||
}
|
||||
// Get the natural zoom level of the projection itself when
|
||||
// zoomed to view the full world
|
||||
var worldBounds = projection.def && projection.def.bounds, worldZoom = (worldBounds &&
|
||||
zoomFromBounds(worldBounds, _this.playingField)) || -Infinity;
|
||||
// Panning rotates the globe
|
||||
if (projection.options.name === 'Orthographic' &&
|
||||
// ... but don't rotate if we're loading only a part of the
|
||||
// world
|
||||
(_this.minZoom || Infinity) < worldZoom * 1.1) {
|
||||
// Empirical ratio where the globe rotates roughly the same
|
||||
// speed as moving the pointer across the center of the
|
||||
// projection
|
||||
var ratio = 440 / (_this.getScale() * Math.min(chart.plotWidth, chart.plotHeight));
|
||||
if (mouseDownRotation) {
|
||||
var lon = (mouseDownX - chartX) * ratio -
|
||||
mouseDownRotation[0], lat = clamp(-mouseDownRotation[1] -
|
||||
(mouseDownY - chartY) * ratio, -80, 80), zoom = _this.zoom;
|
||||
_this.update({
|
||||
projection: {
|
||||
rotation: [-lon, -lat]
|
||||
}
|
||||
}, false);
|
||||
_this.zoom = zoom;
|
||||
chart.redraw(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var scale = _this.getScale();
|
||||
var newCenter = _this.projection.inverse([
|
||||
mouseDownCenterProjected[0] +
|
||||
(mouseDownX - chartX) / scale,
|
||||
mouseDownCenterProjected[1] -
|
||||
(mouseDownY - chartY) / scale
|
||||
]);
|
||||
_this.setView(newCenter, void 0, true, false);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
addEvent(chart, 'pan', onPan);
|
||||
addEvent(chart, 'touchpan', onPan);
|
||||
// Perform the map zoom by selection
|
||||
addEvent(chart, 'selection', function (evt) {
|
||||
// Zoom in
|
||||
if (!evt.resetSelection) {
|
||||
var x = evt.x - chart.plotLeft;
|
||||
var y = evt.y - chart.plotTop;
|
||||
var _a = _this.pixelsToProjectedUnits({ x: x, y: y }), y1 = _a.y, x1 = _a.x;
|
||||
var _b = _this.pixelsToProjectedUnits({ x: x + evt.width, y: y + evt.height }), y2 = _b.y, x2 = _b.x;
|
||||
_this.fitToBounds({ x1: x1, y1: y1, x2: x2, y2: y2 }, void 0, true, evt.originalEvent.touches ?
|
||||
// On touch zoom, don't animate, since we're already in
|
||||
// transformed zoom preview
|
||||
false :
|
||||
// On mouse zoom, obey the chart-level animation
|
||||
void 0);
|
||||
// Only for mouse. Touch users can pinch out.
|
||||
if (!/^touch/.test((evt.originalEvent.type))) {
|
||||
chart.showResetZoom();
|
||||
}
|
||||
evt.preventDefault();
|
||||
// Reset zoom
|
||||
}
|
||||
else {
|
||||
_this.zoomBy();
|
||||
}
|
||||
});
|
||||
};
|
||||
MapView.prototype.render = function () {
|
||||
// We need a group for the insets
|
||||
if (!this.group) {
|
||||
this.group = this.chart.renderer.g('map-view')
|
||||
.attr({ zIndex: 4 })
|
||||
.add();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Update the view with given options
|
||||
*
|
||||
* @function Highcharts.MapView#update
|
||||
*
|
||||
* @param {Partial<Highcharts.MapViewOptions>} options
|
||||
* The new map view options to apply
|
||||
* @param {boolean} [redraw=true]
|
||||
* Whether to redraw immediately
|
||||
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
|
||||
* The animation to apply to a the redraw
|
||||
*/
|
||||
MapView.prototype.update = function (options, redraw, animation) {
|
||||
if (redraw === void 0) { redraw = true; }
|
||||
var newProjection = options.projection;
|
||||
var isDirtyProjection = newProjection && ((Projection.toString(newProjection) !==
|
||||
Projection.toString(this.options.projection))), isDirtyInsets = false;
|
||||
merge(true, this.userOptions, options);
|
||||
merge(true, this.options, options);
|
||||
// If anything changed with the insets, destroy them all and create
|
||||
// again below
|
||||
if ('insets' in options) {
|
||||
this.insets.forEach(function (inset) { return inset.destroy(); });
|
||||
this.insets.length = 0;
|
||||
isDirtyInsets = true;
|
||||
}
|
||||
if (isDirtyProjection || isDirtyInsets) {
|
||||
this.chart.series.forEach(function (series) {
|
||||
var groups = series.transformGroups;
|
||||
if (series.clearBounds) {
|
||||
series.clearBounds();
|
||||
}
|
||||
series.isDirty = true;
|
||||
series.isDirtyData = true;
|
||||
// Destroy inset transform groups
|
||||
if (isDirtyInsets && groups) {
|
||||
while (groups.length > 1) {
|
||||
var group = groups.pop();
|
||||
if (group) {
|
||||
group.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (isDirtyProjection) {
|
||||
this.projection = new Projection(this.options.projection);
|
||||
}
|
||||
// Create new insets
|
||||
if (isDirtyInsets) {
|
||||
this.createInsets();
|
||||
}
|
||||
// Fit to natural bounds if center/zoom are not explicitly given
|
||||
if (!options.center && !isNumber(options.zoom)) {
|
||||
this.fitToBounds(void 0, void 0, false);
|
||||
}
|
||||
}
|
||||
if (options.center || isNumber(options.zoom)) {
|
||||
this.setView(this.options.center, options.zoom, false);
|
||||
}
|
||||
if (redraw) {
|
||||
this.chart.redraw(animation);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Zoom the map view by a given number
|
||||
*
|
||||
* @function Highcharts.MapView#zoomBy
|
||||
*
|
||||
* @param {number|undefined} [howMuch]
|
||||
* The amount of zoom to apply. 1 zooms in on half the current view,
|
||||
* -1 zooms out. Pass `undefined` to zoom to the full bounds of the
|
||||
* map.
|
||||
* @param {Highcharts.LonLatArray} [coords]
|
||||
* Optional map coordinates to keep fixed
|
||||
* @param {Array<number>} [chartCoords]
|
||||
* Optional chart coordinates to keep fixed, in pixels
|
||||
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
|
||||
* The animation to apply to a the redraw
|
||||
*/
|
||||
MapView.prototype.zoomBy = function (howMuch, coords, chartCoords, animation) {
|
||||
var chart = this.chart;
|
||||
var projectedCenter = this.projection.forward(this.center);
|
||||
// let { x, y } = coords || {};
|
||||
var _a = coords ? this.projection.forward(coords) : [], x = _a[0], y = _a[1];
|
||||
if (typeof howMuch === 'number') {
|
||||
var zoom = this.zoom + howMuch;
|
||||
var center = void 0;
|
||||
// Keep chartX and chartY stationary - convert to lat and lng
|
||||
if (chartCoords) {
|
||||
var chartX = chartCoords[0], chartY = chartCoords[1];
|
||||
var scale = this.getScale();
|
||||
var offsetX = chartX - chart.plotLeft - chart.plotWidth / 2;
|
||||
var offsetY = chartY - chart.plotTop - chart.plotHeight / 2;
|
||||
x = projectedCenter[0] + offsetX / scale;
|
||||
y = projectedCenter[1] + offsetY / scale;
|
||||
}
|
||||
// Keep lon and lat stationary by adjusting the center
|
||||
if (typeof x === 'number' && typeof y === 'number') {
|
||||
var scale = 1 - Math.pow(2, this.zoom) / Math.pow(2, zoom);
|
||||
// const projectedCenter = this.projection.forward(this.center);
|
||||
var offsetX = projectedCenter[0] - x;
|
||||
var offsetY = projectedCenter[1] - y;
|
||||
projectedCenter[0] -= offsetX * scale;
|
||||
projectedCenter[1] += offsetY * scale;
|
||||
center = this.projection.inverse(projectedCenter);
|
||||
}
|
||||
this.setView(center, zoom, void 0, animation);
|
||||
// Undefined howMuch => reset zoom
|
||||
}
|
||||
else {
|
||||
this.fitToBounds(void 0, void 0, void 0, animation);
|
||||
}
|
||||
};
|
||||
/* *
|
||||
* Return the composite bounding box of a collection of bounding boxes
|
||||
*/
|
||||
MapView.compositeBounds = function (arrayOfBounds) {
|
||||
if (arrayOfBounds.length) {
|
||||
return arrayOfBounds
|
||||
.slice(1)
|
||||
.reduce(function (acc, cur) {
|
||||
acc.x1 = Math.min(acc.x1, cur.x1);
|
||||
acc.y1 = Math.min(acc.y1, cur.y1);
|
||||
acc.x2 = Math.max(acc.x2, cur.x2);
|
||||
acc.y2 = Math.max(acc.y2, cur.y2);
|
||||
return acc;
|
||||
}, merge(arrayOfBounds[0]));
|
||||
}
|
||||
return;
|
||||
};
|
||||
return MapView;
|
||||
}());
|
||||
// Putting this in the same file due to circular dependency with MapView
|
||||
var MapViewInset = /** @class */ (function (_super) {
|
||||
__extends(MapViewInset, _super);
|
||||
function MapViewInset(mapView, options) {
|
||||
var _this = _super.call(this, mapView.chart, options) || this;
|
||||
_this.id = options.id;
|
||||
_this.mapView = mapView;
|
||||
_this.options = merge(defaultInsetsOptions, options);
|
||||
_this.allBounds = [];
|
||||
if (_this.options.geoBounds) {
|
||||
// The path in projected units in the map view's main projection.
|
||||
// This is used for hit testing where the points should render.
|
||||
var path = mapView.projection.path(_this.options.geoBounds);
|
||||
_this.geoBoundsProjectedBox = boundsFromPath(path);
|
||||
_this.geoBoundsProjectedPolygon = path.map(function (segment) { return [
|
||||
segment[1] || 0,
|
||||
segment[2] || 0
|
||||
]; });
|
||||
}
|
||||
return _this;
|
||||
}
|
||||
// Get the playing field in pixels
|
||||
MapViewInset.prototype.getField = function (padded) {
|
||||
if (padded === void 0) { padded = true; }
|
||||
var hitZone = this.hitZone;
|
||||
if (hitZone) {
|
||||
var padding = padded ? this.padding : [0, 0, 0, 0], polygon = hitZone.coordinates[0], xs = polygon.map(function (xy) { return xy[0]; }), ys = polygon.map(function (xy) { return xy[1]; }), x = Math.min.apply(0, xs) + padding[3], x2 = Math.max.apply(0, xs) - padding[1], y = Math.min.apply(0, ys) + padding[0], y2 = Math.max.apply(0, ys) - padding[2];
|
||||
if (isNumber(x) && isNumber(y)) {
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
width: x2 - x,
|
||||
height: y2 - y
|
||||
};
|
||||
}
|
||||
}
|
||||
// Fall back to plot area
|
||||
return _super.prototype.getField.call(this, padded);
|
||||
};
|
||||
// Get the hit zone in pixels
|
||||
MapViewInset.prototype.getHitZone = function () {
|
||||
var _a = this, chart = _a.chart, mapView = _a.mapView, options = _a.options, coordinates = (options.field || {}).coordinates;
|
||||
if (coordinates) {
|
||||
var polygon = coordinates[0];
|
||||
if (options.units === 'percent') {
|
||||
var relativeTo_1 = options.relativeTo === 'mapBoundingBox' &&
|
||||
mapView.getMapBBox() ||
|
||||
merge(chart.plotBox, { x: 0, y: 0 });
|
||||
polygon = polygon.map(function (xy) { return [
|
||||
relativeLength("".concat(xy[0], "%"), relativeTo_1.width, relativeTo_1.x),
|
||||
relativeLength("".concat(xy[1], "%"), relativeTo_1.height, relativeTo_1.y)
|
||||
]; });
|
||||
}
|
||||
return {
|
||||
type: 'Polygon',
|
||||
coordinates: [polygon]
|
||||
};
|
||||
}
|
||||
};
|
||||
MapViewInset.prototype.getProjectedBounds = function () {
|
||||
return MapView.compositeBounds(this.allBounds);
|
||||
};
|
||||
// Determine whether a point on the main projected plane is inside the
|
||||
// geoBounds of the inset.
|
||||
MapViewInset.prototype.isInside = function (point) {
|
||||
var _a = this, geoBoundsProjectedBox = _a.geoBoundsProjectedBox, geoBoundsProjectedPolygon = _a.geoBoundsProjectedPolygon;
|
||||
return Boolean(
|
||||
// First we do a pre-pass to check whether the test point is inside
|
||||
// the rectangular bounding box of the polygon. This is less
|
||||
// expensive and will rule out most cases.
|
||||
geoBoundsProjectedBox &&
|
||||
point.x >= geoBoundsProjectedBox.x1 &&
|
||||
point.x <= geoBoundsProjectedBox.x2 &&
|
||||
point.y >= geoBoundsProjectedBox.y1 &&
|
||||
point.y <= geoBoundsProjectedBox.y2 &&
|
||||
// Next, do the more expensive check whether the point is inside the
|
||||
// polygon itself.
|
||||
geoBoundsProjectedPolygon &&
|
||||
pointInPolygon(point, geoBoundsProjectedPolygon));
|
||||
};
|
||||
// Render the map view inset with the border path
|
||||
MapViewInset.prototype.render = function () {
|
||||
var _a = this, chart = _a.chart, mapView = _a.mapView, options = _a.options, borderPath = options.borderPath || options.field;
|
||||
if (borderPath && mapView.group) {
|
||||
var animate = true;
|
||||
if (!this.border) {
|
||||
this.border = chart.renderer
|
||||
.path()
|
||||
.addClass('highcharts-mapview-inset-border')
|
||||
.add(mapView.group);
|
||||
animate = false;
|
||||
}
|
||||
if (!chart.styledMode) {
|
||||
this.border.attr({
|
||||
stroke: options.borderColor,
|
||||
'stroke-width': options.borderWidth
|
||||
});
|
||||
}
|
||||
var crisp_1 = Math.round(this.border.strokeWidth()) % 2 / 2, field_1 = (options.relativeTo === 'mapBoundingBox' &&
|
||||
mapView.getMapBBox()) || mapView.playingField;
|
||||
var d = (borderPath.coordinates || []).reduce(function (d, lineString) {
|
||||
return lineString.reduce(function (d, point, i) {
|
||||
var x = point[0], y = point[1];
|
||||
if (options.units === 'percent') {
|
||||
x = chart.plotLeft + relativeLength("".concat(x, "%"), field_1.width, field_1.x);
|
||||
y = chart.plotTop + relativeLength("".concat(y, "%"), field_1.height, field_1.y);
|
||||
}
|
||||
x = Math.floor(x) + crisp_1;
|
||||
y = Math.floor(y) + crisp_1;
|
||||
d.push(i === 0 ? ['M', x, y] : ['L', x, y]);
|
||||
return d;
|
||||
}, d);
|
||||
}, []);
|
||||
// Apply the border path
|
||||
this.border[animate ? 'animate' : 'attr']({ d: d });
|
||||
}
|
||||
};
|
||||
MapViewInset.prototype.destroy = function () {
|
||||
if (this.border) {
|
||||
this.border = this.border.destroy();
|
||||
}
|
||||
this.eventsToUnbind.forEach(function (f) { return f(); });
|
||||
};
|
||||
// No chart-level events for insets
|
||||
MapViewInset.prototype.setUpEvents = function () { };
|
||||
return MapViewInset;
|
||||
}(MapView));
|
||||
// Initialize the MapView after initialization, but before firstRender
|
||||
addEvent(MapChart, 'afterInit', function () {
|
||||
this.mapView = new MapView(this, this.options.mapView);
|
||||
});
|
||||
export default MapView;
|
||||
@@ -0,0 +1,148 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
/**
|
||||
* Generic options for the placement and appearance of map insets like
|
||||
* non-contiguous territories.
|
||||
*
|
||||
* @since 10.0.0
|
||||
* @product highmaps
|
||||
* @optionparent mapView.insetOptions
|
||||
*/
|
||||
var defaultOptions = {
|
||||
/**
|
||||
* The border color of the insets.
|
||||
*
|
||||
* @sample maps/mapview/insetoptions-border
|
||||
* Inset border options
|
||||
* @type {Highcharts.ColorType}
|
||||
*/
|
||||
borderColor: "#cccccc" /* Palette.neutralColor20 */,
|
||||
/**
|
||||
* The pixel border width of the insets.
|
||||
*
|
||||
* @sample maps/mapview/insetoptions-border
|
||||
* Inset border options
|
||||
*/
|
||||
borderWidth: 1,
|
||||
/**
|
||||
* @ignore-option
|
||||
*/
|
||||
center: [0, 0],
|
||||
/**
|
||||
* The padding of the insets. Can be either a number of pixels, a percentage
|
||||
* string, or an array of either. If an array is given, it sets the top,
|
||||
* right, bottom, left paddings respectively.
|
||||
*
|
||||
* @type {number|string|Array<number|string>}
|
||||
*/
|
||||
padding: '10%',
|
||||
/**
|
||||
* What coordinate system the `field` and `borderPath` should relate to. If
|
||||
* `plotBox`, they will be fixed to the plot box and responsively move in
|
||||
* relation to the main map. If `mapBoundingBox`, they will be fixed to the
|
||||
* map bounding box, which is constant and centered in different chart sizes
|
||||
* and ratios.
|
||||
*
|
||||
* @validvalue ["plotBox", "mapBoundingBox"]
|
||||
*/
|
||||
relativeTo: 'mapBoundingBox',
|
||||
/**
|
||||
* What units to use for the `field` and `borderPath` geometries. If
|
||||
* `percent` (default), they relate to the box given in `relativeTo`. If
|
||||
* `pixels`, they are absolute values.
|
||||
*
|
||||
* @validvalue ["percent", "pixels"]
|
||||
*/
|
||||
units: 'percent'
|
||||
};
|
||||
/**
|
||||
* The individual MapView insets, typically used for non-contiguous areas of a
|
||||
* country. Each item inherits from the generic `insetOptions`.
|
||||
*
|
||||
* Some of the TopoJSON files of the [Highcharts Map
|
||||
* Collection](https://code.highcharts.com/mapdata/) include a property called
|
||||
* `hc-recommended-mapview`, and some of these include insets. In order to
|
||||
* override the recommended inset options, an inset option with a matching id
|
||||
* can be applied, and it will be merged into the embedded settings.
|
||||
*
|
||||
* @sample maps/mapview/insets-extended
|
||||
* Extending the embedded insets
|
||||
* @sample maps/mapview/insets-complete
|
||||
* Complete inset config from scratch
|
||||
*
|
||||
* @extends mapView.insetOptions
|
||||
* @type Array<Object>
|
||||
* @product highmaps
|
||||
* @apioption mapView.insets
|
||||
*/
|
||||
/**
|
||||
* A geometry object of type `MultiLineString` defining the border path of the
|
||||
* inset in terms of `units`. If undefined, a border is rendered around the
|
||||
* `field` geometry. It is recommended that the `borderPath` partly follows the
|
||||
* outline of the `field` in order to make pointer positioning consistent.
|
||||
*
|
||||
* @sample maps/mapview/insets-complete
|
||||
* Complete inset config with `borderPath`
|
||||
*
|
||||
* @product highmaps
|
||||
* @type {Object|undefined}
|
||||
* @apioption mapView.insets.borderPath
|
||||
*/
|
||||
/**
|
||||
* A geometry object of type `Polygon` defining where in the chart the inset
|
||||
* should be rendered, in terms of `units` and relative to the `relativeTo`
|
||||
* setting. If a `borderPath` is omitted, a border is rendered around the field.
|
||||
* If undefined, the inset is rendered in the full plot area.
|
||||
*
|
||||
* @sample maps/mapview/insets-extended
|
||||
* Border path emitted, field is rendered
|
||||
*
|
||||
* @product highmaps
|
||||
* @type {Object|undefined}
|
||||
* @apioption mapView.insets.field
|
||||
*/
|
||||
/**
|
||||
* A geometry object of type `Polygon` encircling the shapes that should be
|
||||
* rendered in the inset, in terms of geographic coordinates. Geometries within
|
||||
* this geometry are removed from the default map view and rendered in the
|
||||
* inset.
|
||||
*
|
||||
* @sample maps/mapview/insets-complete
|
||||
* Complete inset config with `geoBounds`
|
||||
*
|
||||
* @product highmaps
|
||||
* @type {Object}
|
||||
* @apioption mapView.insets.geoBounds
|
||||
*/
|
||||
/**
|
||||
* The id of the inset, used for internal reference.
|
||||
*
|
||||
* @sample maps/mapview/insets-extended
|
||||
* Extending recommended insets by id
|
||||
*
|
||||
* @product highmaps
|
||||
* @type {string}
|
||||
* @apioption mapView.insets.id
|
||||
*/
|
||||
/**
|
||||
* The projection options for the inset.
|
||||
*
|
||||
* @product highmaps
|
||||
* @type {Object}
|
||||
* @extends mapView.projection
|
||||
* @apioption mapView.insets.projection
|
||||
*/
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default defaultOptions;
|
||||
@@ -0,0 +1,137 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
/**
|
||||
* The `mapView` options control the initial view of the chart, and how
|
||||
* projection is set up for raw geoJSON maps (beta as of v9.3).
|
||||
*
|
||||
* To set the view dynamically after chart generation, see
|
||||
* [mapView.setView](/class-reference/Highcharts.MapView#setView).
|
||||
*
|
||||
* @since 9.3.0
|
||||
* @product highmaps
|
||||
* @optionparent mapView
|
||||
*/
|
||||
var defaultOptions = {
|
||||
/**
|
||||
* The center of the map in terms of longitude and latitude. For
|
||||
* preprojected maps (like the GeoJSON files in Map Collection v1.x), the
|
||||
* units are projected x and y units.
|
||||
*
|
||||
* @default [0, 0]
|
||||
* @type {Highcharts.LonLatArray}
|
||||
*
|
||||
* @sample {highmaps} maps/mapview/center-zoom Custom view of a world map
|
||||
* @sample {highmaps} maps/mapview/get-view Report the current view of a
|
||||
* preprojected map
|
||||
*/
|
||||
center: [0, 0],
|
||||
/**
|
||||
* Prevents the end user from zooming too far in on the map. See
|
||||
* [zoom](#mapView.zoom).
|
||||
*
|
||||
* @type {number|undefined}
|
||||
*
|
||||
* @sample {highmaps} maps/mapview/maxzoom
|
||||
* Prevent zooming in too far
|
||||
*/
|
||||
maxZoom: void 0,
|
||||
/**
|
||||
* The padding inside the plot area when auto fitting to the map bounds. A
|
||||
* number signifies pixels, and a percentage is relative to the plot area
|
||||
* size.
|
||||
*
|
||||
* @sample {highmaps} maps/chart/plotbackgroundcolor-color
|
||||
* Visible plot area and percentage padding
|
||||
* @type {number|string|Array<number|string>}
|
||||
*/
|
||||
padding: 0,
|
||||
/**
|
||||
* The projection options allow applying client side projection to a map
|
||||
* given in geographic coordinates, typically from TopoJSON or GeoJSON.
|
||||
*
|
||||
* @type {Object}
|
||||
*
|
||||
* @sample maps/mapview/projection-explorer
|
||||
* Projection explorer
|
||||
* @sample maps/demo/topojson-projection
|
||||
* Orthographic projection
|
||||
* @sample maps/mapview/projection-custom-proj4js
|
||||
* Custom UTM projection definition
|
||||
* @sample maps/mapview/projection-custom-d3geo
|
||||
* Custom Robinson projection definition
|
||||
*/
|
||||
projection: {
|
||||
/**
|
||||
* Projection name. Built-in projections are `EqualEarth`,
|
||||
* `LambertConformalConic`, `Miller`, `Orthographic` and `WebMercator`.
|
||||
*
|
||||
* @type {string}
|
||||
* @sample maps/mapview/projection-explorer
|
||||
* Projection explorer
|
||||
* @sample maps/mapview/projection-custom-proj4js
|
||||
* Custom UTM projection definition
|
||||
* @sample maps/mapview/projection-custom-d3geo
|
||||
* Custom Robinson projection definition
|
||||
* @sample maps/demo/topojson-projection
|
||||
* Orthographic projection
|
||||
*/
|
||||
name: void 0,
|
||||
/**
|
||||
* The two standard parallels that define the map layout in conic
|
||||
* projections, like the LambertConformalConic projection. If only one
|
||||
* number is given, the second parallel will be the same as the first.
|
||||
*
|
||||
* @sample maps/mapview/projection-parallels
|
||||
* LCC projection with parallels
|
||||
* @sample maps/mapview/projection-explorer
|
||||
* Projection explorer
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
parallels: void 0,
|
||||
/**
|
||||
* Rotation of the projection in terms of degrees `[lambda, phi,
|
||||
* gamma]`. When given, a three-axis spherical rotation is be applied
|
||||
* to the globe prior to the projection.
|
||||
*
|
||||
* * `lambda` shifts the longitudes by the given value.
|
||||
* * `phi` shifts the latitudes by the given value. Can be omitted.
|
||||
* * `gamma` applies a _roll_. Can be omitted.
|
||||
*
|
||||
* @sample maps/mapview/projection-explorer
|
||||
* Projection explorer
|
||||
* @sample maps/mapview/projection-america-centric
|
||||
* America-centric world map
|
||||
*/
|
||||
rotation: void 0
|
||||
},
|
||||
/**
|
||||
* The zoom level of a map. Higher zoom levels means more zoomed in. An
|
||||
* increase of 1 zooms in to a quarter of the viewed area (half the width
|
||||
* and height). Defaults to fitting to the map bounds.
|
||||
*
|
||||
* In a `WebMercator` projection, a zoom level of 0 represents
|
||||
* the world in a 256x256 pixel square. This is a common concept for WMS
|
||||
* tiling software.
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @sample {highmaps} maps/mapview/center-zoom
|
||||
* Custom view of a world map
|
||||
* @sample {highmaps} maps/mapview/get-view
|
||||
* Report the current view of a preprojected map
|
||||
*/
|
||||
zoom: void 0
|
||||
};
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default defaultOptions;
|
||||
530
static/lib/Highcharts-10.2.1/es-modules/Maps/Projection.js
Normal file
530
static/lib/Highcharts-10.2.1/es-modules/Maps/Projection.js
Normal file
@@ -0,0 +1,530 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
||||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
||||
if (ar || !(i in from)) {
|
||||
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
||||
ar[i] = from[i];
|
||||
}
|
||||
}
|
||||
return to.concat(ar || Array.prototype.slice.call(from));
|
||||
};
|
||||
import PC from '../Core/Geometry/PolygonClip.js';
|
||||
var clipLineString = PC.clipLineString, clipPolygon = PC.clipPolygon;
|
||||
import registry from './Projections/ProjectionRegistry.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var clamp = U.clamp, erase = U.erase;
|
||||
var deg2rad = Math.PI * 2 / 360;
|
||||
// Safe padding on either side of the antimeridian to avoid points being
|
||||
// projected to the wrong side of the plane
|
||||
var floatCorrection = 0.000001;
|
||||
// Keep longitude within -180 and 180. This is faster than using the modulo
|
||||
// operator, and preserves the distinction between -180 and 180.
|
||||
var wrapLon = function (lon) {
|
||||
// Replacing the if's with while would increase the range, but make it prone
|
||||
// to crashes on bad data
|
||||
if (lon < -180) {
|
||||
lon += 360;
|
||||
}
|
||||
if (lon > 180) {
|
||||
lon -= 360;
|
||||
}
|
||||
return lon;
|
||||
};
|
||||
var Projection = /** @class */ (function () {
|
||||
function Projection(options) {
|
||||
if (options === void 0) { options = {}; }
|
||||
// Whether the chart has points, lines or polygons given as coordinates
|
||||
// with positive up, as opposed to paths in the SVG plane with positive
|
||||
// down.
|
||||
this.hasCoordinates = false;
|
||||
// Whether the chart has true projection as opposed to pre-projected geojson
|
||||
// as in the legacy map collection.
|
||||
this.hasGeoProjection = false;
|
||||
this.maxLatitude = 90;
|
||||
this.options = options;
|
||||
var name = options.name, projectedBounds = options.projectedBounds, rotation = options.rotation;
|
||||
this.rotator = rotation ? this.getRotator(rotation) : void 0;
|
||||
var ProjectionDefinition = name ? Projection.registry[name] : void 0;
|
||||
if (ProjectionDefinition) {
|
||||
this.def = new ProjectionDefinition(options);
|
||||
}
|
||||
var _a = this, def = _a.def, rotator = _a.rotator;
|
||||
if (def) {
|
||||
this.maxLatitude = def.maxLatitude || 90;
|
||||
this.hasGeoProjection = true;
|
||||
}
|
||||
if (rotator && def) {
|
||||
this.forward = function (lonLat) {
|
||||
return def.forward(rotator.forward(lonLat));
|
||||
};
|
||||
this.inverse = function (xy) {
|
||||
return rotator.inverse(def.inverse(xy));
|
||||
};
|
||||
}
|
||||
else if (def) {
|
||||
this.forward = function (lonLat) { return def.forward(lonLat); };
|
||||
this.inverse = function (xy) { return def.inverse(xy); };
|
||||
}
|
||||
else if (rotator) {
|
||||
this.forward = rotator.forward;
|
||||
this.inverse = rotator.inverse;
|
||||
}
|
||||
// Projected bounds/clipping
|
||||
this.bounds = projectedBounds === 'world' ?
|
||||
def && def.bounds :
|
||||
projectedBounds;
|
||||
}
|
||||
// Add a projection definition to the registry, accessible by its `name`.
|
||||
Projection.add = function (name, definition) {
|
||||
Projection.registry[name] = definition;
|
||||
};
|
||||
// Calculate the great circle between two given coordinates
|
||||
Projection.greatCircle = function (point1, point2, inclusive) {
|
||||
var atan2 = Math.atan2, cos = Math.cos, sin = Math.sin, sqrt = Math.sqrt;
|
||||
var lat1 = point1[1] * deg2rad;
|
||||
var lon1 = point1[0] * deg2rad;
|
||||
var lat2 = point2[1] * deg2rad;
|
||||
var lon2 = point2[0] * deg2rad;
|
||||
var deltaLat = lat2 - lat1;
|
||||
var deltaLng = lon2 - lon1;
|
||||
var calcA = sin(deltaLat / 2) * sin(deltaLat / 2) +
|
||||
cos(lat1) * cos(lat2) * sin(deltaLng / 2) * sin(deltaLng / 2);
|
||||
var calcB = 2 * atan2(sqrt(calcA), sqrt(1 - calcA));
|
||||
var distance = calcB * 6371e3; // in meters
|
||||
var jumps = Math.round(distance / 500000); // 500 km each jump
|
||||
var lineString = [];
|
||||
if (inclusive) {
|
||||
lineString.push(point1);
|
||||
}
|
||||
if (jumps > 1) {
|
||||
var step = 1 / jumps;
|
||||
for (var fraction = step; fraction < 0.999; // Account for float errors
|
||||
fraction += step) {
|
||||
var A = sin((1 - fraction) * calcB) / sin(calcB);
|
||||
var B = sin(fraction * calcB) / sin(calcB);
|
||||
var x = A * cos(lat1) * cos(lon1) + B * cos(lat2) * cos(lon2);
|
||||
var y = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2);
|
||||
var z = A * sin(lat1) + B * sin(lat2);
|
||||
var lat3 = atan2(z, sqrt(x * x + y * y));
|
||||
var lon3 = atan2(y, x);
|
||||
lineString.push([lon3 / deg2rad, lat3 / deg2rad]);
|
||||
}
|
||||
}
|
||||
if (inclusive) {
|
||||
lineString.push(point2);
|
||||
}
|
||||
return lineString;
|
||||
};
|
||||
Projection.insertGreatCircles = function (poly) {
|
||||
var i = poly.length - 1;
|
||||
while (i--) {
|
||||
// Distance in degrees, either in lon or lat. Avoid heavy
|
||||
// calculation of true distance.
|
||||
var roughDistance = Math.max(Math.abs(poly[i][0] - poly[i + 1][0]), Math.abs(poly[i][1] - poly[i + 1][1]));
|
||||
if (roughDistance > 10) {
|
||||
var greatCircle = Projection.greatCircle(poly[i], poly[i + 1]);
|
||||
if (greatCircle.length) {
|
||||
poly.splice.apply(poly, __spreadArray([i + 1, 0], greatCircle, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Projection.toString = function (options) {
|
||||
var _a = options || {}, name = _a.name, rotation = _a.rotation;
|
||||
return [name, rotation && rotation.join(',')].join(';');
|
||||
};
|
||||
Projection.prototype.lineIntersectsBounds = function (line) {
|
||||
var _a = this.bounds || {}, x1 = _a.x1, x2 = _a.x2, y1 = _a.y1, y2 = _a.y2;
|
||||
var getIntersect = function (line, dim, val) {
|
||||
var p1 = line[0], p2 = line[1], otherDim = dim ? 0 : 1;
|
||||
// Check if points are on either side of the line
|
||||
if (typeof val === 'number' && p1[dim] >= val !== p2[dim] >= val) {
|
||||
var fraction = ((val - p1[dim]) / (p2[dim] - p1[dim])), crossingVal = p1[otherDim] +
|
||||
fraction * (p2[otherDim] - p1[otherDim]);
|
||||
return dim ? [crossingVal, val] : [val, crossingVal];
|
||||
}
|
||||
};
|
||||
var intersection, ret = line[0];
|
||||
if ((intersection = getIntersect(line, 0, x1))) {
|
||||
ret = intersection;
|
||||
// Assuming line[1] was originally outside, replace it with the
|
||||
// intersection point so that the horizontal intersection will
|
||||
// be correct.
|
||||
line[1] = intersection;
|
||||
}
|
||||
else if ((intersection = getIntersect(line, 0, x2))) {
|
||||
ret = intersection;
|
||||
line[1] = intersection;
|
||||
}
|
||||
if ((intersection = getIntersect(line, 1, y1))) {
|
||||
ret = intersection;
|
||||
}
|
||||
else if ((intersection = getIntersect(line, 1, y2))) {
|
||||
ret = intersection;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
/*
|
||||
* Take the rotation options and return the appropriate projection functions
|
||||
*/
|
||||
Projection.prototype.getRotator = function (rotation) {
|
||||
var deltaLambda = rotation[0] * deg2rad, deltaPhi = (rotation[1] || 0) * deg2rad, deltaGamma = (rotation[2] || 0) * deg2rad;
|
||||
var cosDeltaPhi = Math.cos(deltaPhi), sinDeltaPhi = Math.sin(deltaPhi), cosDeltaGamma = Math.cos(deltaGamma), sinDeltaGamma = Math.sin(deltaGamma);
|
||||
if (deltaLambda === 0 && deltaPhi === 0 && deltaGamma === 0) {
|
||||
// Don't waste processing time
|
||||
return;
|
||||
}
|
||||
return {
|
||||
forward: function (lonLat) {
|
||||
// Lambda (lon) rotation
|
||||
var lon = lonLat[0] * deg2rad + deltaLambda;
|
||||
// Phi (lat) and gamma rotation
|
||||
var lat = lonLat[1] * deg2rad, cosLat = Math.cos(lat), x = Math.cos(lon) * cosLat, y = Math.sin(lon) * cosLat, sinLat = Math.sin(lat), k = sinLat * cosDeltaPhi + x * sinDeltaPhi;
|
||||
return [
|
||||
Math.atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - sinLat * sinDeltaPhi) / deg2rad,
|
||||
Math.asin(k * cosDeltaGamma + y * sinDeltaGamma) / deg2rad
|
||||
];
|
||||
},
|
||||
inverse: function (rLonLat) {
|
||||
// Lambda (lon) unrotation
|
||||
var lon = rLonLat[0] * deg2rad;
|
||||
// Phi (lat) and gamma unrotation
|
||||
var lat = rLonLat[1] * deg2rad, cosLat = Math.cos(lat), x = Math.cos(lon) * cosLat, y = Math.sin(lon) * cosLat, sinLat = Math.sin(lat), k = sinLat * cosDeltaGamma - y * sinDeltaGamma;
|
||||
return [
|
||||
(Math.atan2(y * cosDeltaGamma + sinLat * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi) - deltaLambda) / deg2rad,
|
||||
Math.asin(k * cosDeltaPhi - x * sinDeltaPhi) / deg2rad
|
||||
];
|
||||
}
|
||||
};
|
||||
};
|
||||
// Project a lonlat coordinate position to xy. Dynamically overridden when
|
||||
// projection is set.
|
||||
Projection.prototype.forward = function (lonLat) {
|
||||
return lonLat;
|
||||
};
|
||||
// Unproject an xy chart coordinate position to lonlat. Dynamically
|
||||
// overridden when projection is set.
|
||||
Projection.prototype.inverse = function (xy) {
|
||||
return xy;
|
||||
};
|
||||
Projection.prototype.cutOnAntimeridian = function (poly, isPolygon) {
|
||||
var antimeridian = 180;
|
||||
var intersections = [];
|
||||
var polygons = [poly];
|
||||
poly.forEach(function (lonLat, i) {
|
||||
var previousLonLat = poly[i - 1];
|
||||
if (!i) {
|
||||
if (!isPolygon) {
|
||||
return;
|
||||
}
|
||||
// Else, wrap to beginning
|
||||
previousLonLat = poly[poly.length - 1];
|
||||
}
|
||||
var lon1 = previousLonLat[0], lon2 = lonLat[0];
|
||||
if (
|
||||
// Both points, after rotating for antimeridian, are on the far
|
||||
// side of the Earth
|
||||
(lon1 < -90 || lon1 > 90) &&
|
||||
(lon2 < -90 || lon2 > 90) &&
|
||||
// ... and on either side of the plane
|
||||
(lon1 > 0) !== (lon2 > 0)) {
|
||||
// Interpolate to the intersection latitude
|
||||
var fraction = clamp((antimeridian - (lon1 + 360) % 360) /
|
||||
((lon2 + 360) % 360 - (lon1 + 360) % 360), 0, 1), lat = (previousLonLat[1] +
|
||||
fraction * (lonLat[1] - previousLonLat[1]));
|
||||
intersections.push({
|
||||
i: i,
|
||||
lat: lat,
|
||||
direction: lon1 < 0 ? 1 : -1,
|
||||
previousLonLat: previousLonLat,
|
||||
lonLat: lonLat
|
||||
});
|
||||
}
|
||||
});
|
||||
var polarIntersection;
|
||||
if (intersections.length) {
|
||||
if (isPolygon) {
|
||||
// Simplified use of the even-odd rule, if there is an odd
|
||||
// amount of intersections between the polygon and the
|
||||
// antimeridian, the pole is inside the polygon. Applies
|
||||
// primarily to Antarctica.
|
||||
if (intersections.length % 2 === 1) {
|
||||
polarIntersection = intersections.slice().sort(function (a, b) { return Math.abs(b.lat) - Math.abs(a.lat); })[0];
|
||||
erase(intersections, polarIntersection);
|
||||
}
|
||||
// Pull out slices of the polygon that is on the opposite side
|
||||
// of the antimeridian compared to the starting point
|
||||
var i = intersections.length - 2;
|
||||
while (i >= 0) {
|
||||
var index = intersections[i].i;
|
||||
var lonPlus = wrapLon(antimeridian +
|
||||
intersections[i].direction * floatCorrection);
|
||||
var lonMinus = wrapLon(antimeridian -
|
||||
intersections[i].direction * floatCorrection);
|
||||
var slice = poly.splice.apply(poly, __spreadArray([index,
|
||||
intersections[i + 1].i - index], Projection.greatCircle([lonPlus, intersections[i].lat], [lonPlus, intersections[i + 1].lat], true), false));
|
||||
// Add interpolated points close to the cut
|
||||
slice.push.apply(slice, Projection.greatCircle([lonMinus, intersections[i + 1].lat], [lonMinus, intersections[i].lat], true));
|
||||
polygons.push(slice);
|
||||
i -= 2;
|
||||
}
|
||||
// Insert dummy points close to the pole
|
||||
if (polarIntersection) {
|
||||
for (var i_1 = 0; i_1 < polygons.length; i_1++) {
|
||||
var direction = polarIntersection.direction, lat = polarIntersection.lat, poly_1 = polygons[i_1], indexOf = poly_1.indexOf(polarIntersection.lonLat);
|
||||
if (indexOf > -1) {
|
||||
var polarLatitude = (lat < 0 ? -1 : 1) *
|
||||
this.maxLatitude;
|
||||
var lon1 = wrapLon(antimeridian +
|
||||
direction * floatCorrection);
|
||||
var lon2 = wrapLon(antimeridian -
|
||||
direction * floatCorrection);
|
||||
var polarSegment = Projection.greatCircle([lon1, lat], [lon1, polarLatitude], true);
|
||||
// Circle around the pole point in order to make
|
||||
// polygon clipping right. Without this, Antarctica
|
||||
// would wrap the wrong way in an LLC projection
|
||||
// with parallels [30, 40].
|
||||
for (var lon = lon1 + 120 * direction; lon > -180 && lon < 180; lon += 120 * direction) {
|
||||
polarSegment.push([lon, polarLatitude]);
|
||||
}
|
||||
polarSegment.push.apply(polarSegment, Projection.greatCircle([lon2, polarLatitude], [lon2, polarIntersection.lat], true));
|
||||
poly_1.splice.apply(poly_1, __spreadArray([indexOf,
|
||||
0], polarSegment, false));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Map lines, not closed
|
||||
}
|
||||
else {
|
||||
var i = intersections.length;
|
||||
while (i--) {
|
||||
var index = intersections[i].i;
|
||||
var slice = poly.splice(index, poly.length,
|
||||
// Add interpolated point close to the cut
|
||||
[
|
||||
wrapLon(antimeridian +
|
||||
intersections[i].direction * floatCorrection),
|
||||
intersections[i].lat
|
||||
]);
|
||||
// Add interpolated point close to the cut
|
||||
slice.unshift([
|
||||
wrapLon(antimeridian -
|
||||
intersections[i].direction * floatCorrection),
|
||||
intersections[i].lat
|
||||
]);
|
||||
polygons.push(slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
return polygons;
|
||||
};
|
||||
// Take a GeoJSON geometry and return a translated SVGPath
|
||||
Projection.prototype.path = function (geometry) {
|
||||
var _this = this;
|
||||
var _a = this, bounds = _a.bounds, def = _a.def, rotator = _a.rotator;
|
||||
var antimeridian = 180;
|
||||
var path = [];
|
||||
var isPolygon = geometry.type === 'Polygon' ||
|
||||
geometry.type === 'MultiPolygon';
|
||||
// @todo: It doesn't really have to do with whether north is
|
||||
// positive. It depends on whether the coordinates are
|
||||
// pre-projected.
|
||||
var hasGeoProjection = this.hasGeoProjection;
|
||||
// Detect whether we need to do antimeridian cutting and clipping to
|
||||
// bounds. The alternative (currently for Orthographic) is to apply a
|
||||
// clip angle.
|
||||
var projectingToPlane = !def || def.antimeridianCutting !== false;
|
||||
// We need to rotate in a separate step before applying antimeridian
|
||||
// cutting
|
||||
var preclip = projectingToPlane ? rotator : void 0;
|
||||
var postclip = projectingToPlane ? (def || this) : this;
|
||||
var boundsPolygon;
|
||||
if (bounds) {
|
||||
boundsPolygon = [
|
||||
[bounds.x1, bounds.y1],
|
||||
[bounds.x2, bounds.y1],
|
||||
[bounds.x2, bounds.y2],
|
||||
[bounds.x1, bounds.y2]
|
||||
];
|
||||
}
|
||||
var addToPath = function (polygon) {
|
||||
// Create a copy of the original coordinates. The copy applies a
|
||||
// correction of points close to the antimeridian in order to
|
||||
// prevent the points to be projected to the wrong side of the
|
||||
// plane. Float errors in topojson or in the projection may cause
|
||||
// that.
|
||||
var poly = polygon.map(function (lonLat) {
|
||||
if (projectingToPlane) {
|
||||
if (preclip) {
|
||||
lonLat = preclip.forward(lonLat);
|
||||
}
|
||||
var lon = lonLat[0];
|
||||
if (Math.abs(lon - antimeridian) < floatCorrection) {
|
||||
if (lon < antimeridian) {
|
||||
lon = antimeridian - floatCorrection;
|
||||
}
|
||||
else {
|
||||
lon = antimeridian + floatCorrection;
|
||||
}
|
||||
}
|
||||
lonLat = [lon, lonLat[1]];
|
||||
}
|
||||
return lonLat;
|
||||
});
|
||||
var polygons = [poly];
|
||||
if (hasGeoProjection) {
|
||||
// Insert great circles into long straight lines
|
||||
Projection.insertGreatCircles(poly);
|
||||
if (projectingToPlane) {
|
||||
polygons = _this.cutOnAntimeridian(poly, isPolygon);
|
||||
}
|
||||
}
|
||||
polygons.forEach(function (poly) {
|
||||
if (poly.length < 2) {
|
||||
return;
|
||||
}
|
||||
var movedTo = false;
|
||||
var firstValidLonLat;
|
||||
var lastValidLonLat;
|
||||
var gap = false;
|
||||
var pushToPath = function (point) {
|
||||
if (!movedTo) {
|
||||
path.push(['M', point[0], point[1]]);
|
||||
movedTo = true;
|
||||
}
|
||||
else {
|
||||
path.push(['L', point[0], point[1]]);
|
||||
}
|
||||
};
|
||||
var someOutside = false, someInside = false;
|
||||
var points = poly.map(function (lonLat) {
|
||||
var xy = postclip.forward(lonLat);
|
||||
if (xy.outside) {
|
||||
someOutside = true;
|
||||
}
|
||||
else {
|
||||
someInside = true;
|
||||
}
|
||||
// Mercator projects pole points to Infinity, and
|
||||
// clipPolygon is not able to handle it.
|
||||
if (xy[1] === Infinity) {
|
||||
xy[1] = 10e9;
|
||||
}
|
||||
else if (xy[1] === -Infinity) {
|
||||
xy[1] = -10e9;
|
||||
}
|
||||
return xy;
|
||||
});
|
||||
if (projectingToPlane) {
|
||||
// Wrap around in order for pointInPolygon to work
|
||||
if (isPolygon) {
|
||||
points.push(points[0]);
|
||||
}
|
||||
if (someOutside) {
|
||||
// All points are outside
|
||||
if (!someInside) {
|
||||
return;
|
||||
}
|
||||
// Some inside, some outside. Clip to the bounds.
|
||||
if (boundsPolygon) {
|
||||
// Polygons
|
||||
if (isPolygon) {
|
||||
points = clipPolygon(points, boundsPolygon);
|
||||
// Linestrings
|
||||
}
|
||||
else if (bounds) {
|
||||
clipLineString(points, boundsPolygon)
|
||||
.forEach(function (points) {
|
||||
movedTo = false;
|
||||
points.forEach(pushToPath);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
points.forEach(pushToPath);
|
||||
// For orthographic projection, or when a clipAngle applies
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
var lonLat = poly[i], point = points[i];
|
||||
if (!point.outside) {
|
||||
// In order to be able to interpolate if the first
|
||||
// or last point is invalid (on the far side of the
|
||||
// globe in an orthographic projection), we need to
|
||||
// push the first valid point to the end of the
|
||||
// polygon.
|
||||
if (isPolygon && !firstValidLonLat) {
|
||||
firstValidLonLat = lonLat;
|
||||
poly.push(lonLat);
|
||||
points.push(point);
|
||||
}
|
||||
// When entering the first valid point after a gap
|
||||
// of invalid points, typically on the far side of
|
||||
// the globe in an orthographic projection.
|
||||
if (gap && lastValidLonLat) {
|
||||
// For areas, in an orthographic projection, the
|
||||
// great circle between two visible points will
|
||||
// be close to the horizon. A possible exception
|
||||
// may be when the two points are on opposite
|
||||
// sides of the globe. It that poses a problem,
|
||||
// we may have to rewrite this to use the small
|
||||
// circle related to the current lon0 and lat0.
|
||||
if (isPolygon && hasGeoProjection) {
|
||||
var greatCircle = Projection.greatCircle(lastValidLonLat, lonLat);
|
||||
greatCircle.forEach(function (lonLat) {
|
||||
return pushToPath(postclip.forward(lonLat));
|
||||
});
|
||||
// For lines, just jump over the gap
|
||||
}
|
||||
else {
|
||||
movedTo = false;
|
||||
}
|
||||
}
|
||||
pushToPath(point);
|
||||
lastValidLonLat = lonLat;
|
||||
gap = false;
|
||||
}
|
||||
else {
|
||||
gap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
if (geometry.type === 'LineString') {
|
||||
addToPath(geometry.coordinates);
|
||||
}
|
||||
else if (geometry.type === 'MultiLineString') {
|
||||
geometry.coordinates.forEach(function (c) { return addToPath(c); });
|
||||
}
|
||||
else if (geometry.type === 'Polygon') {
|
||||
geometry.coordinates.forEach(function (c) { return addToPath(c); });
|
||||
if (path.length) {
|
||||
path.push(['Z']);
|
||||
}
|
||||
}
|
||||
else if (geometry.type === 'MultiPolygon') {
|
||||
geometry.coordinates.forEach(function (polygons) {
|
||||
polygons.forEach(function (c) { return addToPath(c); });
|
||||
});
|
||||
if (path.length) {
|
||||
path.push(['Z']);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
};
|
||||
Projection.registry = registry;
|
||||
return Projection;
|
||||
}());
|
||||
export default Projection;
|
||||
@@ -0,0 +1,51 @@
|
||||
/* *
|
||||
*
|
||||
* Equal Earth projection, an equal-area projection designed to minimize
|
||||
* distortion and remain pleasing to the eye.
|
||||
*
|
||||
* Invented by Bojan Šavrič, Bernhard Jenny, and Tom Patterson in 2018. It is
|
||||
* inspired by the widely used Robinson projection.
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
var A1 = 1.340264, A2 = -0.081106, A3 = 0.000893, A4 = 0.003796, M = Math.sqrt(3) / 2.0, scale = 74.03120656864502;
|
||||
var EqualEarth = /** @class */ (function () {
|
||||
function EqualEarth() {
|
||||
this.bounds = {
|
||||
x1: -200.37508342789243,
|
||||
x2: 200.37508342789243,
|
||||
y1: -97.52595454902263,
|
||||
y2: 97.52595454902263
|
||||
};
|
||||
}
|
||||
EqualEarth.prototype.forward = function (lonLat) {
|
||||
var d = Math.PI / 180, paramLat = Math.asin(M * Math.sin(lonLat[1] * d)), paramLatSq = paramLat * paramLat, paramLatPow6 = paramLatSq * paramLatSq * paramLatSq;
|
||||
var x = lonLat[0] * d * Math.cos(paramLat) * scale / (M *
|
||||
(A1 +
|
||||
3 * A2 * paramLatSq +
|
||||
paramLatPow6 * (7 * A3 + 9 * A4 * paramLatSq)));
|
||||
var y = paramLat * scale * (A1 + A2 * paramLatSq + paramLatPow6 * (A3 + A4 * paramLatSq));
|
||||
return [x, y];
|
||||
};
|
||||
EqualEarth.prototype.inverse = function (xy) {
|
||||
var x = xy[0] / scale, y = xy[1] / scale, d = 180 / Math.PI, epsilon = 1e-9, iterations = 12;
|
||||
var paramLat = y, paramLatSq, paramLatPow6, fy, fpy, dlat, i;
|
||||
for (i = 0; i < iterations; ++i) {
|
||||
paramLatSq = paramLat * paramLat;
|
||||
paramLatPow6 = paramLatSq * paramLatSq * paramLatSq;
|
||||
fy = paramLat * (A1 + A2 * paramLatSq + paramLatPow6 * (A3 + A4 * paramLatSq)) - y;
|
||||
fpy = A1 + 3 * A2 * paramLatSq + paramLatPow6 * (7 * A3 + 9 * A4 * paramLatSq);
|
||||
paramLat -= dlat = fy / fpy;
|
||||
if (Math.abs(dlat) < epsilon) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
paramLatSq = paramLat * paramLat;
|
||||
paramLatPow6 = paramLatSq * paramLatSq * paramLatSq;
|
||||
var lon = d * M * x * (A1 + 3 * A2 * paramLatSq + paramLatPow6 * (7 * A3 + 9 * A4 * paramLatSq)) / Math.cos(paramLat);
|
||||
var lat = d * Math.asin(Math.sin(paramLat) / M);
|
||||
return [lon, lat];
|
||||
};
|
||||
return EqualEarth;
|
||||
}());
|
||||
export default EqualEarth;
|
||||
@@ -0,0 +1,60 @@
|
||||
/* *
|
||||
* Lambert Conformal Conic projection
|
||||
* */
|
||||
'use strict';
|
||||
var sign = Math.sign ||
|
||||
(function (n) { return (n === 0 ? 0 : n > 0 ? 1 : -1); }), scale = 63.78137, deg2rad = Math.PI / 180, halfPI = Math.PI / 2, eps10 = 1e-6, tany = function (y) { return Math.tan((halfPI + y) / 2); };
|
||||
var LambertConformalConic = /** @class */ (function () {
|
||||
function LambertConformalConic(options) {
|
||||
var _a;
|
||||
var parallels = (options.parallels || [])
|
||||
.map(function (n) { return n * deg2rad; }), lat1 = parallels[0] || 0, lat2 = (_a = parallels[1]) !== null && _a !== void 0 ? _a : lat1, cosLat1 = Math.cos(lat1);
|
||||
if (typeof options.projectedBounds === 'object') {
|
||||
this.projectedBounds = options.projectedBounds;
|
||||
}
|
||||
// Apply the global variables
|
||||
var n = lat1 === lat2 ?
|
||||
Math.sin(lat1) :
|
||||
Math.log(cosLat1 / Math.cos(lat2)) / Math.log(tany(lat2) / tany(lat1));
|
||||
if (Math.abs(n) < 1e-10) {
|
||||
n = (sign(n) || 1) * 1e-10;
|
||||
}
|
||||
this.n = n;
|
||||
this.c = cosLat1 * Math.pow(tany(lat1), n) / n;
|
||||
}
|
||||
LambertConformalConic.prototype.forward = function (lonLat) {
|
||||
var lon = lonLat[0] * deg2rad, _a = this, c = _a.c, n = _a.n, projectedBounds = _a.projectedBounds;
|
||||
var lat = lonLat[1] * deg2rad;
|
||||
if (c > 0) {
|
||||
if (lat < -halfPI + eps10) {
|
||||
lat = -halfPI + eps10;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (lat > halfPI - eps10) {
|
||||
lat = halfPI - eps10;
|
||||
}
|
||||
}
|
||||
var r = c / Math.pow(tany(lat), n), x = r * Math.sin(n * lon) * scale, y = (c - r * Math.cos(n * lon)) * scale, xy = [x, y];
|
||||
if (projectedBounds && (x < projectedBounds.x1 ||
|
||||
x > projectedBounds.x2 ||
|
||||
y < projectedBounds.y1 ||
|
||||
y > projectedBounds.y2)) {
|
||||
xy.outside = true;
|
||||
}
|
||||
return xy;
|
||||
};
|
||||
LambertConformalConic.prototype.inverse = function (xy) {
|
||||
var x = xy[0] / scale, y = xy[1] / scale, _a = this, c = _a.c, n = _a.n, cy = c - y, rho = sign(n) * Math.sqrt(x * x + cy * cy);
|
||||
var l = Math.atan2(x, Math.abs(cy)) * sign(cy);
|
||||
if (cy * n < 0) {
|
||||
l -= Math.PI * sign(x) * sign(cy);
|
||||
}
|
||||
return [
|
||||
(l / n) / deg2rad,
|
||||
(2 * Math.atan(Math.pow(c / rho, 1 / n)) - halfPI) / deg2rad
|
||||
];
|
||||
};
|
||||
return LambertConformalConic;
|
||||
}());
|
||||
export default LambertConformalConic;
|
||||
@@ -0,0 +1,29 @@
|
||||
/* *
|
||||
* Miller projection
|
||||
* */
|
||||
'use strict';
|
||||
var quarterPI = Math.PI / 4, deg2rad = Math.PI / 180, scale = 63.78137;
|
||||
var Miller = /** @class */ (function () {
|
||||
function Miller() {
|
||||
this.bounds = {
|
||||
x1: -200.37508342789243,
|
||||
x2: 200.37508342789243,
|
||||
y1: -146.91480769173063,
|
||||
y2: 146.91480769173063
|
||||
};
|
||||
}
|
||||
Miller.prototype.forward = function (lonLat) {
|
||||
return [
|
||||
lonLat[0] * deg2rad * scale,
|
||||
1.25 * scale * Math.log(Math.tan(quarterPI + 0.4 * lonLat[1] * deg2rad))
|
||||
];
|
||||
};
|
||||
Miller.prototype.inverse = function (xy) {
|
||||
return [
|
||||
(xy[0] / scale) / deg2rad,
|
||||
2.5 * (Math.atan(Math.exp(0.8 * (xy[1] / scale))) - quarterPI) / deg2rad
|
||||
];
|
||||
};
|
||||
return Miller;
|
||||
}());
|
||||
export default Miller;
|
||||
@@ -0,0 +1,37 @@
|
||||
/* *
|
||||
* Orthographic projection
|
||||
* */
|
||||
'use strict';
|
||||
var deg2rad = Math.PI / 180, scale = 63.78460826781007;
|
||||
var Orthographic = /** @class */ (function () {
|
||||
function Orthographic() {
|
||||
this.antimeridianCutting = false;
|
||||
this.bounds = {
|
||||
x1: -scale,
|
||||
x2: scale,
|
||||
y1: -scale,
|
||||
y2: scale
|
||||
};
|
||||
}
|
||||
Orthographic.prototype.forward = function (lonLat) {
|
||||
var lonDeg = lonLat[0], latDeg = lonLat[1];
|
||||
var lat = latDeg * deg2rad;
|
||||
var xy = [
|
||||
Math.cos(lat) * Math.sin(lonDeg * deg2rad) * scale,
|
||||
Math.sin(lat) * scale
|
||||
];
|
||||
if (lonDeg < -90 || lonDeg > 90) {
|
||||
xy.outside = true;
|
||||
}
|
||||
return xy;
|
||||
};
|
||||
Orthographic.prototype.inverse = function (xy) {
|
||||
var x = xy[0] / scale, y = xy[1] / scale, z = Math.sqrt(x * x + y * y), c = Math.asin(z), cSin = Math.sin(c), cCos = Math.cos(c);
|
||||
return [
|
||||
Math.atan2(x * cSin, z * cCos) / deg2rad,
|
||||
Math.asin(z && y * cSin / z) / deg2rad
|
||||
];
|
||||
};
|
||||
return Orthographic;
|
||||
}());
|
||||
export default Orthographic;
|
||||
@@ -0,0 +1,19 @@
|
||||
/* *
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import LambertConformalConic from './LambertConformalConic.js';
|
||||
import EqualEarth from './EqualEarth.js';
|
||||
import Miller from './Miller.js';
|
||||
import Orthographic from './Orthographic.js';
|
||||
import WebMercator from './WebMercator.js';
|
||||
var registry = {
|
||||
EqualEarth: EqualEarth,
|
||||
LambertConformalConic: LambertConformalConic,
|
||||
Miller: Miller,
|
||||
Orthographic: Orthographic,
|
||||
WebMercator: WebMercator
|
||||
};
|
||||
export default registry;
|
||||
@@ -0,0 +1,36 @@
|
||||
/* *
|
||||
* Web Mercator projection, used for most online map tile services
|
||||
* */
|
||||
'use strict';
|
||||
var maxLatitude = 85.0511287798, // The latitude that defines a square
|
||||
r = 63.78137, deg2rad = Math.PI / 180;
|
||||
var WebMercator = /** @class */ (function () {
|
||||
function WebMercator() {
|
||||
this.bounds = {
|
||||
x1: -200.37508342789243,
|
||||
x2: 200.37508342789243,
|
||||
y1: -200.3750834278071,
|
||||
y2: 200.3750834278071
|
||||
};
|
||||
this.maxLatitude = maxLatitude;
|
||||
}
|
||||
WebMercator.prototype.forward = function (lonLat) {
|
||||
var sinLat = Math.sin(lonLat[1] * deg2rad);
|
||||
var xy = [
|
||||
r * lonLat[0] * deg2rad,
|
||||
r * Math.log((1 + sinLat) / (1 - sinLat)) / 2
|
||||
];
|
||||
if (Math.abs(lonLat[1]) > maxLatitude) {
|
||||
xy.outside = true;
|
||||
}
|
||||
return xy;
|
||||
};
|
||||
WebMercator.prototype.inverse = function (xy) {
|
||||
return [
|
||||
xy[0] / (r * deg2rad),
|
||||
(2 * Math.atan(Math.exp(xy[1] / r)) - (Math.PI / 2)) / deg2rad
|
||||
];
|
||||
};
|
||||
return WebMercator;
|
||||
}());
|
||||
export default WebMercator;
|
||||
Reference in New Issue
Block a user