MapBox.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
  2. /**
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. */
  20. /* eslint-disable react/jsx-sort-default-props, react/sort-prop-types */
  21. /* eslint-disable react/forbid-prop-types, react/require-default-props */
  22. import React from 'react';
  23. import PropTypes from 'prop-types';
  24. import MapGL from 'react-map-gl';
  25. import Immutable from 'immutable';
  26. import ViewportMercator from 'viewport-mercator-project';
  27. import ScatterPlotGlowOverlay from './ScatterPlotGlowOverlay';
  28. import './MapBox.css';
  29. const NOOP = () => {};
  30. export const DEFAULT_MAX_ZOOM = 16;
  31. export const DEFAULT_POINT_RADIUS = 60;
  32. const propTypes = {
  33. width: PropTypes.number,
  34. height: PropTypes.number,
  35. aggregatorName: PropTypes.string,
  36. clusterer: PropTypes.object,
  37. globalOpacity: PropTypes.number,
  38. hasCustomMetric: PropTypes.bool,
  39. mapStyle: PropTypes.string,
  40. mapboxApiKey: PropTypes.string.isRequired,
  41. onViewportChange: PropTypes.func,
  42. pointRadius: PropTypes.number,
  43. pointRadiusUnit: PropTypes.string,
  44. renderWhileDragging: PropTypes.bool,
  45. rgb: PropTypes.array,
  46. bounds: PropTypes.array
  47. };
  48. const defaultProps = {
  49. width: 400,
  50. height: 400,
  51. globalOpacity: 1,
  52. onViewportChange: NOOP,
  53. pointRadius: DEFAULT_POINT_RADIUS,
  54. pointRadiusUnit: 'Pixels'
  55. };
  56. class MapBox extends React.Component {
  57. constructor(props) {
  58. super(props);
  59. const {
  60. width,
  61. height,
  62. bounds
  63. } = this.props; // Get a viewport that fits the given bounds, which all marks to be clustered.
  64. // Derive lat, lon and zoom from this viewport. This is only done on initial
  65. // render as the bounds don't update as we pan/zoom in the current design.
  66. const mercator = new ViewportMercator({
  67. width,
  68. height
  69. }).fitBounds(bounds);
  70. const {
  71. latitude,
  72. longitude,
  73. zoom
  74. } = mercator;
  75. this.state = {
  76. viewport: {
  77. longitude,
  78. latitude,
  79. zoom
  80. }
  81. };
  82. this.handleViewportChange = this.handleViewportChange.bind(this);
  83. }
  84. handleViewportChange(viewport) {
  85. this.setState({
  86. viewport
  87. });
  88. const {
  89. onViewportChange
  90. } = this.props;
  91. onViewportChange(viewport);
  92. }
  93. render() {
  94. const {
  95. width,
  96. height,
  97. aggregatorName,
  98. clusterer,
  99. globalOpacity,
  100. mapStyle,
  101. mapboxApiKey,
  102. pointRadius,
  103. pointRadiusUnit,
  104. renderWhileDragging,
  105. rgb,
  106. hasCustomMetric,
  107. bounds
  108. } = this.props;
  109. const {
  110. viewport
  111. } = this.state;
  112. const isDragging = viewport.isDragging === undefined ? false : viewport.isDragging; // Compute the clusters based on the original bounds and current zoom level. Note when zoom/pan
  113. // to an area outside of the original bounds, no additional queries are made to the backend to
  114. // retrieve additional data.
  115. const bbox = [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]];
  116. const clusters = clusterer.getClusters(bbox, Math.round(viewport.zoom));
  117. return React.createElement(MapGL, _extends({}, viewport, {
  118. mapStyle: mapStyle,
  119. width: width,
  120. height: height,
  121. mapboxApiAccessToken: mapboxApiKey,
  122. onViewportChange: this.handleViewportChange
  123. }), React.createElement(ScatterPlotGlowOverlay, _extends({}, viewport, {
  124. isDragging: isDragging,
  125. locations: Immutable.fromJS(clusters),
  126. dotRadius: pointRadius,
  127. pointRadiusUnit: pointRadiusUnit,
  128. rgb: rgb,
  129. globalOpacity: globalOpacity,
  130. compositeOperation: "screen",
  131. renderWhileDragging: renderWhileDragging,
  132. aggregation: hasCustomMetric ? aggregatorName : null,
  133. lngLatAccessor: location => {
  134. const coordinates = location.get('geometry').get('coordinates');
  135. return [coordinates.get(0), coordinates.get(1)];
  136. }
  137. })));
  138. }
  139. }
  140. MapBox.propTypes = propTypes;
  141. MapBox.defaultProps = defaultProps;
  142. export default MapBox;