GeoJSON.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /* *
  2. *
  3. * (c) 2010-2019 Torstein Honsi
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8. *
  9. * */
  10. 'use strict';
  11. import H from '../parts/Globals.js';
  12. /**
  13. * Result object of a map transformation.
  14. *
  15. * @interface Highcharts.MapCoordinateObject
  16. */ /**
  17. * X coordinate on the map.
  18. * @name Highcharts.MapCoordinateObject#x
  19. * @type {number}
  20. */ /**
  21. * Y coordinate on the map.
  22. * @name Highcharts.MapCoordinateObject#y
  23. * @type {number|null}
  24. */
  25. /**
  26. * A latitude/longitude object.
  27. *
  28. * @interface Highcharts.MapLatLonObject
  29. */ /**
  30. * The latitude.
  31. * @name Highcharts.MapLatLonObject#lat
  32. * @type {number}
  33. */ /**
  34. * The longitude.
  35. * @name Highcharts.MapLatLonObject#lon
  36. * @type {number}
  37. */
  38. import U from '../parts/Utilities.js';
  39. var extend = U.extend, wrap = U.wrap;
  40. import '../parts/Options.js';
  41. import '../parts/Chart.js';
  42. var Chart = H.Chart, format = H.format, merge = H.merge, win = H.win;
  43. /* eslint-disable no-invalid-this, valid-jsdoc */
  44. /**
  45. * Test for point in polygon. Polygon defined as array of [x,y] points.
  46. * @private
  47. */
  48. function pointInPolygon(point, polygon) {
  49. var i, j, rel1, rel2, c = false, x = point.x, y = point.y;
  50. for (i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
  51. rel1 = polygon[i][1] > y;
  52. rel2 = polygon[j][1] > y;
  53. if (rel1 !== rel2 &&
  54. (x < (polygon[j][0] -
  55. polygon[i][0]) * (y - polygon[i][1]) /
  56. (polygon[j][1] - polygon[i][1]) +
  57. polygon[i][0])) {
  58. c = !c;
  59. }
  60. }
  61. return c;
  62. }
  63. /**
  64. * Highmaps only. Get point from latitude and longitude using specified
  65. * transform definition.
  66. *
  67. * @requires modules/map
  68. *
  69. * @sample maps/series/latlon-transform/
  70. * Use specific transformation for lat/lon
  71. *
  72. * @function Highcharts.Chart#transformFromLatLon
  73. *
  74. * @param {Highcharts.MapLatLonObject} latLon
  75. * A latitude/longitude object.
  76. *
  77. * @param {*} transform
  78. * The transform definition to use as explained in the
  79. * {@link https://www.highcharts.com/docs/maps/latlon|documentation}.
  80. *
  81. * @return {Highcharts.MapCoordinateObject}
  82. * An object with `x` and `y` properties.
  83. */
  84. Chart.prototype.transformFromLatLon = function (latLon, transform) {
  85. if (typeof win.proj4 === 'undefined') {
  86. H.error(21, false, this);
  87. return {
  88. x: 0,
  89. y: null
  90. };
  91. }
  92. var projected = win.proj4(transform.crs, [latLon.lon, latLon.lat]), cosAngle = transform.cosAngle ||
  93. (transform.rotation && Math.cos(transform.rotation)), sinAngle = transform.sinAngle ||
  94. (transform.rotation && Math.sin(transform.rotation)), rotated = transform.rotation ? [
  95. projected[0] * cosAngle + projected[1] * sinAngle,
  96. -projected[0] * sinAngle + projected[1] * cosAngle
  97. ] : projected;
  98. return {
  99. x: ((rotated[0] - (transform.xoffset || 0)) * (transform.scale || 1) +
  100. (transform.xpan || 0)) * (transform.jsonres || 1) +
  101. (transform.jsonmarginX || 0),
  102. y: (((transform.yoffset || 0) - rotated[1]) * (transform.scale || 1) +
  103. (transform.ypan || 0)) * (transform.jsonres || 1) -
  104. (transform.jsonmarginY || 0)
  105. };
  106. };
  107. /**
  108. * Highmaps only. Get latLon from point using specified transform definition.
  109. * The method returns an object with the numeric properties `lat` and `lon`.
  110. *
  111. * @requires modules/map
  112. *
  113. * @sample maps/series/latlon-transform/
  114. * Use specific transformation for lat/lon
  115. *
  116. * @function Highcharts.Chart#transformToLatLon
  117. *
  118. * @param {Highcharts.Point|Highcharts.MapCoordinateObject} point
  119. * A `Point` instance, or any object containing the properties `x` and
  120. * `y` with numeric values.
  121. *
  122. * @param {*} transform
  123. * The transform definition to use as explained in the
  124. * {@link https://www.highcharts.com/docs/maps/latlon|documentation}.
  125. *
  126. * @return {Highcharts.MapLatLonObject|undefined}
  127. * An object with `lat` and `lon` properties.
  128. */
  129. Chart.prototype.transformToLatLon = function (point, transform) {
  130. if (typeof win.proj4 === 'undefined') {
  131. H.error(21, false, this);
  132. return;
  133. }
  134. var normalized = {
  135. x: ((point.x -
  136. (transform.jsonmarginX || 0)) / (transform.jsonres || 1) -
  137. (transform.xpan || 0)) / (transform.scale || 1) +
  138. (transform.xoffset || 0),
  139. y: ((-point.y - (transform.jsonmarginY || 0)) / (transform.jsonres || 1) +
  140. (transform.ypan || 0)) / (transform.scale || 1) +
  141. (transform.yoffset || 0)
  142. }, cosAngle = transform.cosAngle ||
  143. (transform.rotation && Math.cos(transform.rotation)), sinAngle = transform.sinAngle ||
  144. (transform.rotation && Math.sin(transform.rotation)),
  145. // Note: Inverted sinAngle to reverse rotation direction
  146. projected = win.proj4(transform.crs, 'WGS84', transform.rotation ? {
  147. x: normalized.x * cosAngle + normalized.y * -sinAngle,
  148. y: normalized.x * sinAngle + normalized.y * cosAngle
  149. } : normalized);
  150. return { lat: projected.y, lon: projected.x };
  151. };
  152. /**
  153. * Highmaps only. Calculate latitude/longitude values for a point. Returns an
  154. * object with the numeric properties `lat` and `lon`.
  155. *
  156. * @requires modules/map
  157. *
  158. * @sample maps/demo/latlon-advanced/
  159. * Advanced lat/lon demo
  160. *
  161. * @function Highcharts.Chart#fromPointToLatLon
  162. *
  163. * @param {Highcharts.Point|Highcharts.MapCoordinateObject} point
  164. * A `Point` instance or anything containing `x` and `y` properties with
  165. * numeric values.
  166. *
  167. * @return {Highcharts.MapLatLonObject|undefined}
  168. * An object with `lat` and `lon` properties.
  169. */
  170. Chart.prototype.fromPointToLatLon = function (point) {
  171. var transforms = this.mapTransforms, transform;
  172. if (!transforms) {
  173. H.error(22, false, this);
  174. return;
  175. }
  176. for (transform in transforms) {
  177. if (Object.hasOwnProperty.call(transforms, transform) &&
  178. transforms[transform].hitZone &&
  179. pointInPolygon({ x: point.x, y: -point.y }, transforms[transform].hitZone.coordinates[0])) {
  180. return this.transformToLatLon(point, transforms[transform]);
  181. }
  182. }
  183. return this.transformToLatLon(point, transforms['default'] // eslint-disable-line dot-notation
  184. );
  185. };
  186. /**
  187. * Highmaps only. Get chart coordinates from latitude/longitude. Returns an
  188. * object with x and y values corresponding to the `xAxis` and `yAxis`.
  189. *
  190. * @requires modules/map
  191. *
  192. * @sample maps/series/latlon-to-point/
  193. * Find a point from lat/lon
  194. *
  195. * @function Highcharts.Chart#fromLatLonToPoint
  196. *
  197. * @param {Highcharts.MapLatLonObject} latLon
  198. * Coordinates.
  199. *
  200. * @return {Highcharts.MapCoordinateObject}
  201. * X and Y coordinates in terms of chart axis values.
  202. */
  203. Chart.prototype.fromLatLonToPoint = function (latLon) {
  204. var transforms = this.mapTransforms, transform, coords;
  205. if (!transforms) {
  206. H.error(22, false, this);
  207. return {
  208. x: 0,
  209. y: null
  210. };
  211. }
  212. for (transform in transforms) {
  213. if (Object.hasOwnProperty.call(transforms, transform) &&
  214. transforms[transform].hitZone) {
  215. coords = this.transformFromLatLon(latLon, transforms[transform]);
  216. if (pointInPolygon({ x: coords.x, y: -coords.y }, transforms[transform].hitZone.coordinates[0])) {
  217. return coords;
  218. }
  219. }
  220. }
  221. return this.transformFromLatLon(latLon, transforms['default'] // eslint-disable-line dot-notation
  222. );
  223. };
  224. /**
  225. * Highmaps only. Restructure a GeoJSON object in preparation to be read
  226. * directly by the
  227. * {@link https://api.highcharts.com/highmaps/plotOptions.series.mapData|series.mapData}
  228. * option. The GeoJSON will be broken down to fit a specific Highcharts type,
  229. * either `map`, `mapline` or `mappoint`. Meta data in GeoJSON's properties
  230. * object will be copied directly over to {@link Point.properties} in Highmaps.
  231. *
  232. * @requires modules/map
  233. *
  234. * @sample maps/demo/geojson/
  235. * Simple areas
  236. * @sample maps/demo/geojson-multiple-types/
  237. * Multiple types
  238. *
  239. * @function Highcharts.geojson
  240. *
  241. * @param {*} geojson
  242. * The GeoJSON structure to parse, represented as a JavaScript object
  243. * rather than a JSON string.
  244. *
  245. * @param {string} [hType=map]
  246. * The Highmaps series type to prepare for. Setting "map" will return
  247. * GeoJSON polygons and multipolygons. Setting "mapline" will return
  248. * GeoJSON linestrings and multilinestrings. Setting "mappoint" will
  249. * return GeoJSON points and multipoints.
  250. *
  251. * @return {Array<*>}
  252. * An object ready for the `mapData` option.
  253. */
  254. H.geojson = function (geojson, hType, series) {
  255. var mapData = [], path = [], polygonToPath = function (polygon) {
  256. var i, len = polygon.length;
  257. path.push('M');
  258. for (i = 0; i < len; i++) {
  259. if (i === 1) {
  260. path.push('L');
  261. }
  262. path.push(polygon[i][0], -polygon[i][1]);
  263. }
  264. };
  265. hType = hType || 'map';
  266. geojson.features.forEach(function (feature) {
  267. var geometry = feature.geometry, type = geometry.type, coordinates = geometry.coordinates, properties = feature.properties, point;
  268. path = [];
  269. if (hType === 'map' || hType === 'mapbubble') {
  270. if (type === 'Polygon') {
  271. coordinates.forEach(polygonToPath);
  272. path.push('Z');
  273. }
  274. else if (type === 'MultiPolygon') {
  275. coordinates.forEach(function (items) {
  276. items.forEach(polygonToPath);
  277. });
  278. path.push('Z');
  279. }
  280. if (path.length) {
  281. point = { path: path };
  282. }
  283. }
  284. else if (hType === 'mapline') {
  285. if (type === 'LineString') {
  286. polygonToPath(coordinates);
  287. }
  288. else if (type === 'MultiLineString') {
  289. coordinates.forEach(polygonToPath);
  290. }
  291. if (path.length) {
  292. point = { path: path };
  293. }
  294. }
  295. else if (hType === 'mappoint') {
  296. if (type === 'Point') {
  297. point = {
  298. x: coordinates[0],
  299. y: -coordinates[1]
  300. };
  301. }
  302. }
  303. if (point) {
  304. mapData.push(extend(point, {
  305. name: properties.name || properties.NAME,
  306. /**
  307. * In Highmaps, when data is loaded from GeoJSON, the GeoJSON
  308. * item's properies are copied over here.
  309. *
  310. * @requires modules/map
  311. * @name Highcharts.Point#properties
  312. * @type {*}
  313. */
  314. properties: properties
  315. }));
  316. }
  317. });
  318. // Create a credits text that includes map source, to be picked up in
  319. // Chart.addCredits
  320. if (series && geojson.copyrightShort) {
  321. series.chart.mapCredits = format(series.chart.options.credits.mapText, { geojson: geojson });
  322. series.chart.mapCreditsFull = format(series.chart.options.credits.mapTextFull, { geojson: geojson });
  323. }
  324. return mapData;
  325. };
  326. // Override addCredits to include map source by default
  327. wrap(Chart.prototype, 'addCredits', function (proceed, credits) {
  328. credits = merge(true, this.options.credits, credits);
  329. // Disable credits link if map credits enabled. This to allow for in-text
  330. // anchors.
  331. if (this.mapCredits) {
  332. credits.href = null;
  333. }
  334. proceed.call(this, credits);
  335. // Add full map credits to hover
  336. if (this.credits && this.mapCreditsFull) {
  337. this.credits.attr({
  338. title: this.mapCreditsFull
  339. });
  340. }
  341. });