77import React , { memo } from 'react' ;
88import _ from 'lodash' ;
99
10- import ReactMapGL , { Marker , Popup } from 'react-map-gl' ;
10+ import ReactMapGL , { Source , Layer , Popup } from 'react-map-gl' ;
1111import axios from 'axios' ;
1212import MapSelect from './mapSelect' ;
1313import { townArray } from './townConstants' ;
1414import CityInfo from './cityInfo' ;
15- import CityPin from './cityPin' ;
1615import Wrapper from './Wrapper' ;
1716
1817/* eslint-disable react/prefer-stateless-function */
@@ -30,14 +29,15 @@ class Map extends React.PureComponent {
3029 } ,
3130 selectedTown : 'Pittsburgh' ,
3231 popupInfo : null ,
33- sites : [ ] ,
3432 } ;
33+
34+ this . handleClick = this . handleClick . bind ( this ) ;
3535 }
3636
3737 componentDidMount ( ) {
3838 axios
3939 . get ( 'https://dev.stevesaylor.io/api/location/' )
40- . then ( res => this . setState ( { sites : res . data } ) ) ;
40+ . then ( res => this . setState ( { geoJSON : this . convertToGeoJSON ( res ) } ) ) ;
4141 }
4242
4343 handleSelection ( event ) {
@@ -55,23 +55,45 @@ class Map extends React.PureComponent {
5555 } ) ;
5656 }
5757
58- renderCityMarker = ( city , index ) => {
59- if ( city . longitude ) {
60- return (
61- < Marker
62- key = { `marker-${ index } ` }
63- longitude = { city . longitude }
64- latitude = { city . latitude }
65- >
66- < CityPin
67- size = { 20 }
68- onClick = { ( ) => this . setState ( { popupInfo : city } ) }
69- />
70- </ Marker >
71- ) ;
72- }
73- return true ;
74- } ;
58+ convertToGeoJSON ( { data } ) {
59+ // Converts api data to geoJSON points
60+ // Might be better if this arrived from the server in this format
61+ return {
62+ type : 'FeatureCollection' ,
63+ features : data . reduce ( ( result , site ) => {
64+ const { latitude, longitude } = site ;
65+ if ( longitude ) {
66+ result . push ( {
67+ type : 'Feature' ,
68+ geometry : {
69+ type : 'Point' ,
70+ // GeoJSON takes lat/lon in reverse order
71+ // Even more confusing: at least half of the lat/lon values provided are reversed??
72+ // Not sure how this wasn't a problem with previous config
73+ coordinates :
74+ longitude < latitude
75+ ? [ longitude , latitude ]
76+ : [ latitude , longitude ] ,
77+ } ,
78+ properties : site ,
79+ } ) ;
80+ }
81+ return result ;
82+ } , [ ] ) ,
83+ } ;
84+ }
85+
86+ handleClick ( event ) {
87+ // Filter out features that we didn't provide
88+ const features = event . features
89+ ? event . features . filter ( feature => feature . layer . id === 'data' )
90+ : [ ] ;
91+ if ( ! features . length ) return ;
92+ // If there are still several features, pick one at random
93+ // Future: might be better to calculate which is closest to cursor
94+ const index = Math . floor ( Math . random ( ) * features . length ) ;
95+ this . setState ( { popupInfo : features [ index ] . properties } ) ;
96+ }
7597
7698 renderPopup ( ) {
7799 const { popupInfo } = this . state ;
@@ -103,10 +125,22 @@ class Map extends React.PureComponent {
103125 < ReactMapGL
104126 { ...this . state . viewport }
105127 onViewportChange = { viewport => this . setState ( { viewport } ) }
128+ onClick = { this . handleClick }
129+ clickRadius = { 10 }
106130 mapboxApiAccessToken = "pk.eyJ1IjoiaHlwZXJmbHVpZCIsImEiOiJjaWpra3Q0MnIwMzRhdGZtNXAwMzRmNXhvIn0.tZzUmF9nGk2h28zx6PM13w"
107131 >
108- { this . state . sites . map ( this . renderCityMarker ) }
109-
132+ { ! ! this . state . geoJSON && (
133+ < Source type = "geojson" data = { this . state . geoJSON } >
134+ < Layer
135+ type = "symbol"
136+ id = "data"
137+ layout = { {
138+ 'icon-image' : 'marker-15' ,
139+ 'icon-allow-overlap' : true ,
140+ } }
141+ />
142+ </ Source >
143+ ) }
110144 { this . renderPopup ( ) }
111145 </ ReactMapGL >
112146 </ Wrapper >
0 commit comments