geoView.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. var __rest = (this && this.__rest) || function (s, e) {
  2. var t = {};
  3. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
  4. t[p] = s[p];
  5. if (s != null && typeof Object.getOwnPropertySymbols === "function")
  6. for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  7. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
  8. t[p[i]] = s[p[i]];
  9. }
  10. return t;
  11. };
  12. import { Linear } from '@antv/scale';
  13. import { upperFirst } from '@antv/util';
  14. import { geoPath, geoGraticule10 } from 'd3-geo';
  15. import { maybeTooltip } from '../utils/mark';
  16. import * as d3Projection from './d3Projection';
  17. /**
  18. * Get projection factory from d3-projection.
  19. */
  20. function normalizeProjection(type) {
  21. if (typeof type === 'function')
  22. return type;
  23. const name = `geo${upperFirst(type)}`;
  24. const projection = d3Projection[name];
  25. if (!projection)
  26. throw new Error(`Unknown coordinate: ${type}`);
  27. return projection;
  28. }
  29. /**
  30. * @see https://github.com/mapbox/geojson-merge/blob/master/index.js
  31. */
  32. function mergeGeoJSON(gjs) {
  33. return {
  34. type: 'FeatureCollection',
  35. features: gjs.flatMap((gj) => normalizeGeoJSON(gj).features),
  36. };
  37. }
  38. function normalizeGeoJSON(gj) {
  39. const types = {
  40. Point: 'geometry',
  41. MultiPoint: 'geometry',
  42. LineString: 'geometry',
  43. MultiLineString: 'geometry',
  44. Polygon: 'geometry',
  45. MultiPolygon: 'geometry',
  46. GeometryCollection: 'geometry',
  47. Feature: 'feature',
  48. FeatureCollection: 'featureCollection',
  49. };
  50. if (!gj || !gj.type)
  51. return null;
  52. const type = types[gj.type];
  53. if (!type)
  54. return null;
  55. if (type === 'geometry') {
  56. return {
  57. type: 'FeatureCollection',
  58. features: [
  59. {
  60. type: 'Feature',
  61. properties: {},
  62. geometry: gj,
  63. },
  64. ],
  65. };
  66. }
  67. else if (type === 'feature') {
  68. return {
  69. type: 'FeatureCollection',
  70. features: [gj],
  71. };
  72. }
  73. else if (type === 'featureCollection') {
  74. return gj;
  75. }
  76. }
  77. /**
  78. * Specify the options for d3 projection
  79. * @see https://github.com/d3/d3-geo#projections
  80. * @todo Specify key each by each.
  81. */
  82. function setProjectionOptions(projection, options) {
  83. var _a;
  84. for (const [key, value] of Object.entries(options)) {
  85. (_a = projection[key]) === null || _a === void 0 ? void 0 : _a.call(projection, value);
  86. }
  87. }
  88. function setProjectionSize(projection, nodes, layout, options) {
  89. const defaultOutline = () => {
  90. const geoNodes = nodes.filter(isGeoPath);
  91. // For geoPath with sphere mark, use it as outline.
  92. const sphere = geoNodes.find((d) => d.sphere);
  93. if (sphere)
  94. return { type: 'Sphere' };
  95. // Merge all GeoJSON as the outline.
  96. return mergeGeoJSON(geoNodes.filter((d) => !d.sphere).flatMap((d) => d.data.value));
  97. };
  98. const { outline = defaultOutline() } = options;
  99. const { size = 'fitExtent' } = options;
  100. if (size === 'fitExtent') {
  101. return setFitExtent(projection, outline, layout);
  102. }
  103. else if (size === 'fitWidth') {
  104. return setFitWidth(projection, outline, layout);
  105. }
  106. }
  107. function setFitExtent(projection, object, layout) {
  108. const { x, y, width, height } = layout;
  109. projection.fitExtent([
  110. [x, y],
  111. [width, height],
  112. ], object);
  113. }
  114. function setFitWidth(projection, object, layout) {
  115. const { width, height } = layout;
  116. const [[x0, y0], [x1, y1]] = geoPath(projection.fitWidth(width, object)).bounds(object);
  117. const dy = Math.ceil(y1 - y0);
  118. const l = Math.min(Math.ceil(x1 - x0), dy);
  119. const s = (projection.scale() * (l - 1)) / l;
  120. const [tx, ty] = projection.translate();
  121. const t = ty + (height - dy) / 2;
  122. projection.scale(s).translate([tx, t]).precision(0.2);
  123. }
  124. /**
  125. * @todo Remove this.
  126. */
  127. function normalizeDataSource(node) {
  128. const { data } = node;
  129. if (Array.isArray(data))
  130. return Object.assign(Object.assign({}, node), { data: { value: data } });
  131. const { type } = data;
  132. if (type === 'graticule10') {
  133. return Object.assign(Object.assign({}, node), { data: { value: [geoGraticule10()] } });
  134. }
  135. else if (type === 'sphere') {
  136. // Sphere is not a standard type of GeoJSON.
  137. // Mark this geoPath as sphere geoPath.
  138. return Object.assign(Object.assign({}, node), { sphere: true, data: { value: [{ type: 'Sphere' }] } });
  139. }
  140. return node;
  141. }
  142. function isGeoPath(d) {
  143. return d.type === 'geoPath';
  144. }
  145. /**
  146. * A view with geo coordinate.
  147. */
  148. export const GeoView = () => {
  149. return (options) => {
  150. const { children, coordinate: projection = {} } = options;
  151. if (!Array.isArray(children))
  152. return [];
  153. // Get projection factory.
  154. const { type = 'equalEarth' } = projection, projectionOptions = __rest(projection, ["type"]);
  155. const createProjection = normalizeProjection(type);
  156. const nodes = children.map(normalizeDataSource);
  157. // Set path generator lazily.
  158. let path;
  159. // A custom geo coordinate.
  160. function Geo() {
  161. return [
  162. [
  163. 'custom',
  164. (x, y, width, height) => {
  165. // Create and set projection.
  166. const visual = createProjection();
  167. const layout = { x, y, width, height };
  168. setProjectionSize(visual, nodes, layout, projectionOptions);
  169. setProjectionOptions(visual, projectionOptions);
  170. // Create path generator.
  171. path = geoPath(visual);
  172. // Normalize projection and projection.invert,
  173. // which normalize projected points.
  174. const scaleX = new Linear({
  175. domain: [x, x + width],
  176. });
  177. const scaleY = new Linear({
  178. domain: [y, y + height],
  179. });
  180. const normalize = (point) => {
  181. const visualPoint = visual(point);
  182. if (!visualPoint)
  183. return null;
  184. const [vx, vy] = visualPoint;
  185. return [scaleX.map(vx), scaleY.map(vy)];
  186. };
  187. const normalizeInvert = (point) => {
  188. if (!point)
  189. return null;
  190. const [px, py] = point;
  191. const visualPoint = [scaleX.invert(px), scaleY.invert(py)];
  192. return visual.invert(visualPoint);
  193. };
  194. return {
  195. transform: (point) => normalize(point),
  196. untransform: (point) => normalizeInvert(point),
  197. };
  198. },
  199. ],
  200. ];
  201. }
  202. function GeoPath(options) {
  203. const { style, tooltip = {} } = options;
  204. return Object.assign(Object.assign({}, options), { type: 'path', tooltip: maybeTooltip(tooltip, {
  205. title: 'id',
  206. items: [{ channel: 'color' }],
  207. }), style: Object.assign(Object.assign({}, style), { d: (d) => path(d) || [] }) });
  208. }
  209. const t = (d) => (isGeoPath(d) ? GeoPath(d) : d);
  210. return [
  211. Object.assign(Object.assign({}, options), { type: 'view', scale: {
  212. x: { type: 'identity' },
  213. y: { type: 'identity' },
  214. }, axis: false, coordinate: { type: Geo }, children: nodes.flatMap(t) }),
  215. ];
  216. };
  217. };
  218. GeoView.props = {};
  219. //# sourceMappingURL=geoView.js.map