MapPage.tsx 8.51 KB
Newer Older
1
import * as React from 'react';
2
import { connect } from 'react-redux';
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
3
import { Link } from 'react-router-dom';
4
5
6
import { translate } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import { bindActionCreators } from 'redux';
7
import { loadAccessionsMapInfo } from 'accessions/actions/public';
8
import AccessionFilter from 'model/accession/AccessionFilter';
9
import Loading from 'ui/common/Loading';
10
import AccessionMapInfo from 'model/accession/AccessionMapInfo';
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
11
import MapLayer from 'model/genesys/MapTileLayer';
12
import Button from '@material-ui/core/Button';
13
import Tabs, { Tab } from 'ui/common/Tabs';
14
import PrettyFilters from 'ui/common/filter/PrettyFilters';
Oleksii Savran's avatar
Oleksii Savran committed
15
import ButtonBar from 'ui/common/buttons/ButtonBar';
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
16
17
import ContentLayout from 'ui/layout/ContentLayout';
import MapConfigSection from './c/MapConfigSection';
18

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
19
import AccessionService from 'service/genesys/AccessionService';
20
21
22

let Map;
let TileLayer;
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
23
24
25
26
let Popup;
let Marker;

const popupContentLimit = 11;
27

28
29
30
31
interface IMapPageProps {
    classes?: any;
    t?: any;

32
    apiUrl: string;
33
    mapInfo: AccessionMapInfo;
34
35
    currentTab: string;
    filterCode: string;
36
    loadAccessionsMapInfo: any;
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
37
    mapLayers: MapLayer[];
38
39
40
41
42
43
44
45
46
47
48
}

const styles = (theme) => ({
    leafletContainer: {
        width: '100%',
        minHeight: '500px',
        height: 'calc(100vh - 186px)',
    },
});

class BrowsePage extends React.Component<IMapPageProps, any> {
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
49
50
51

    private clickTimeout;

52
    protected static needs = [
53
54
      ({ params: { filterCode } }) => {
        return loadAccessionsMapInfo(filterCode || '');
55
56
57
      },
    ];

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
58
59
60
61
62
63
    public state = {
      popupPosition: [],
      geoData: [],
      otherCount: 0,
    };

64
65
66
67
68
    constructor(props, context) {
        super(props, context);
        if (typeof window !== 'undefined') {
            Map = require('react-leaflet').Map;
            TileLayer = require('react-leaflet').TileLayer;
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
69
70
            Marker = require('react-leaflet').Marker;
            Popup = require('react-leaflet').Popup;
71
72
73
74
75
        }
    }


    public componentWillMount() {
76
77
78
79
80
81
82
      const { mapInfo, filterCode, loadAccessionsMapInfo } = this.props;
      // console.log(`Filter code for map ${filterCode} ?== ${mapInfo && mapInfo.filterCode}`, filterCode, mapInfo ? mapInfo.filterCode : 'No mapInfo');
      if (mapInfo && mapInfo.filterCode !== filterCode) {
        // console.log(`mapInfo.filterCode !== filterCode. updatingRoute`);
        loadAccessionsMapInfo(filterCode || '');
      } else if (!mapInfo) {
        loadAccessionsMapInfo(filterCode || '');
83
84
85
      }
    }

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

    private onMapClick = (e) => {
        if (this.clickTimeout) {
            console.log('prevented');
            clearTimeout(this.clickTimeout);
            this.clickTimeout = null;
            return;
        }
        this.clickTimeout = setTimeout(() => {
            console.log('started');
            this.clickTimeout = null;
            const currentZoom = e.target._zoom;
            if (currentZoom < 4) {
                return;
            }

            const {mapInfo: {filter}} = this.props;

            const correctLng = Math.abs(e.latlng.lng) > 180 ? (360 + e.latlng.lng) % 180 : e.latlng.lng;

            console.log(e);

            const filterWithGeo = {
                ...filter,
                geo: {
                    longitude: {
                        ge: correctLng - (3 / currentZoom),
                        le: correctLng + (3 / currentZoom),
                    },
                    latitude: {
                        ge: e.latlng.lat - (3 / currentZoom),
                        le: e.latlng.lat + (3 / currentZoom),
                    },
                },
            };
            AccessionService.geoJson(filterWithGeo, popupContentLimit)
                .then((res) => this.setState({popupPosition: [ e.latlng.lat,  e.latlng.lng], geoData: res.geoJson, otherCount: res.otherCount}));
        }, 200);
    }

126
127
128
129
130
    /// Wrap loadAccessionsMapInfo dispatch and fills the current sort selection
    protected myApplyFilters = (filters: AccessionFilter) => {
      const { loadAccessionsMapInfo } = this.props;

      loadAccessionsMapInfo(filters);
131
132
133
    }

    public render() {
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
134
        const {popupPosition, geoData, otherCount} = this.state;
135
136
        const position = [30, 0];

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
137
        const { mapInfo, mapLayers, currentTab, classes, filterCode, t } = this.props;
138
139
140
141

        if (! mapInfo) {
          return <Loading />;
        }
142
143

        // const color = 'f00ba0';
144
        const layerUrl = `{s}/acn/tile/{z}/{x}/{y}?f=${filterCode ? filterCode : ''}`; // `&color=${color}`;
145
146

        return (
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
147
148
149
150
151
            <ContentLayout
              right={ <MapConfigSection/> }
              rightAlwaysCollapsible
              customHeaderHeight
            >
152
153
154
                <Tabs
                    tab={ currentTab }
                    actions={
Oleksii Savran's avatar
Oleksii Savran committed
155
                      <ButtonBar>
156
                        <span>
Matija Obreza's avatar
Matija Obreza committed
157
158
                          <form method="post" action="/proxy/api/v1/acn/downloadKml">
                            <input type="hidden" name="f" value={ filterCode } />
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
159
                            <Button type="submit">{ `${t('common:action.download')} ${t('accessions.public.p.map.kml')}` }</Button>
Matija Obreza's avatar
Matija Obreza committed
160
                          </form>
161
                        </span>
Oleksii Savran's avatar
Oleksii Savran committed
162
                      </ButtonBar>
163
164
                    }
                >
Viacheslav Pavlov's avatar
i18n    
Viacheslav Pavlov committed
165
166
167
                  <Tab name="data" to={ `/a/${filterCode || ''}` }>{ t('accessions.tab.data') }</Tab>
                  <Tab name="overview" to={ `/a/overview/${filterCode || ''}` }>{ t('accessions.tab.overview') }</Tab>
                  <Tab name="map" to={ `/a/map/${filterCode || '' }` }>{ t('accessions.tab.map') }</Tab>
168
169
170
                </Tabs>
                <PrettyFilters
                    prefix="accessions"
171
                    filterObj={ mapInfo && mapInfo.filter || {} }
172
173
174
                    onSubmit={ this.myApplyFilters }
                />
                <div className={ classes.leafletContainer }>
175
                { mapInfo && typeof window !== 'undefined' &&
176
                    <Map
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
177
                        onClick={ this.onMapClick }
178
                        center={ position }
179
180
                        zoom={ 3 } minZoom={ 2 } maxZoom={ 14 }
                        bounds={ mapInfo.bounds }>
181
                        <TileLayer
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
182
                            zIndex={ 0 }
183
184
185
186
                            opacity={ 0.50 }
                            attribution={ '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }
                            url={ 'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png' }
                        />
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
187
188
189

                        { mapLayers && mapLayers.filter((layer) => layer.enabled).map((layer, index) => <TileLayer zIndex={ index + 1 } key={ layer.name } { ...layer }/>) }

Matija Obreza's avatar
Matija Obreza committed
190
                        <TileLayer
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
191
192

                            zIndex={ mapLayers.length + 1 }
193
194
195
196
                            updateInterval={ 1000 }
                            updateWhenZooming={ false }
                            attribution="&amp;copy Accession localities from <a href=&quot;/&quot;>Genesys PGR</a>"
                            url={ layerUrl }
197
                            subdomains={ mapInfo.tileServers }
198
                        />
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
199
200
201
202
203
204
205
206
207
208
                        { geoData && geoData.length > 0 &&
                            <Marker position={ popupPosition } ref={ (marker) => marker && marker.leafletElement.openPopup() } >
                                <Popup open>
                                    <div>
                                        { geoData.map((feature) => (<div><Link to={ `/a/${feature.properties.uuid}` }>{ `${feature.properties.accessionNumber} ${feature.properties.instCode}` }</Link></div>)) }
                                        { otherCount > 0 && <div>{ t('accessions.public.p.map.andMore', {otherMore: otherCount}) }</div> }
                                    </div>
                                </Popup>
                            </Marker>
                        }
209
210
211
                    </Map>
                }
                </div>
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
212
            </ContentLayout>
213
214
215
216
217
        );
    }
}

const mapStateToProps = (state, ownProps) => ({
218
    mapInfo: state.accessions.public.mapInfo || undefined,
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
219
    mapLayers: state.accessions.public.mapLayers,
220
221
222
223
224
    filterCode: ownProps.match.params.filterCode || '',
    currentTab: ownProps.match.params.tab || 'map', // current tab, or ownProps.location.pathname
});

const mapDispatchToProps = (dispatch) => bindActionCreators({
225
    loadAccessionsMapInfo,
226
227
}, dispatch);

228
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(translate()(BrowsePage)));