MapPage.tsx 8.11 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';
11
import PageLayout from 'ui/layout/PageLayout';
12
import ContentHeader from 'ui/common/heading/ContentHeader';
13
import Button from '@material-ui/core/Button';
14
import Tabs, { Tab } from 'ui/common/Tabs';
15
import PrettyFilters from 'ui/common/filter/PrettyFilters';
Oleksii Savran's avatar
Oleksii Savran committed
16
import ButtonBar from 'ui/common/buttons/ButtonBar';
17

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

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

const popupContentLimit = 11;
26

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

31
    apiUrl: string;
32
    mapInfo: AccessionMapInfo;
33
34
    currentTab: string;
    filterCode: string;
35
    loadAccessionsMapInfo: any;
36
37
38
39
40
41
42
43
44
45
46
}

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
47
48
49

    private clickTimeout;

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

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

62
63
64
65
66
    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
67
68
            Marker = require('react-leaflet').Marker;
            Popup = require('react-leaflet').Popup;
69
70
71
72
73
        }
    }


    public componentWillMount() {
74
75
76
77
78
79
80
      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 || '');
81
82
83
      }
    }

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
84
85
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

    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);
    }

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

      loadAccessionsMapInfo(filters);
129
130
131
    }

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

Viacheslav Pavlov's avatar
i18n    
Viacheslav Pavlov committed
135
        const { mapInfo, currentTab, classes, filterCode, t } = this.props;
136
137
138
139

        if (! mapInfo) {
          return <Loading />;
        }
140
141

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

        return (
Oleksii Savran's avatar
Oleksii Savran committed
145
            <PageLayout withFooter>
Matija Obreza's avatar
Matija Obreza committed
146
              <ContentHeader title={ t('accessions.public.p.browse.title') } subTitle={ t('accessions.public.p.browse.subTitle') } />
147
148
149
                <Tabs
                    tab={ currentTab }
                    actions={
Oleksii Savran's avatar
Oleksii Savran committed
150
                      <ButtonBar>
151
                        <span>
Matija Obreza's avatar
Matija Obreza committed
152
153
                          <form method="post" action="/proxy/api/v1/acn/downloadKml">
                            <input type="hidden" name="f" value={ filterCode } />
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
154
                            <Button type="submit">{ `${t('common:action.download')} ${t('accessions.public.p.map.kml')}` }</Button>
Matija Obreza's avatar
Matija Obreza committed
155
                          </form>
156
                        </span>
Oleksii Savran's avatar
Oleksii Savran committed
157
                      </ButtonBar>
158
159
                    }
                >
Viacheslav Pavlov's avatar
i18n    
Viacheslav Pavlov committed
160
161
162
                  <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>
163
164
165
                </Tabs>
                <PrettyFilters
                    prefix="accessions"
166
                    filterObj={ mapInfo && mapInfo.filter || {} }
167
168
169
                    onSubmit={ this.myApplyFilters }
                />
                <div className={ classes.leafletContainer }>
170
                { mapInfo && typeof window !== 'undefined' &&
171
                    <Map
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
172
                        onClick={ this.onMapClick }
173
                        center={ position }
174
175
                        zoom={ 3 } minZoom={ 2 } maxZoom={ 14 }
                        bounds={ mapInfo.bounds }>
176
177
178
179
180
181
182
183
184
185
                        <TileLayer
                            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' }
                        />
                        <TileLayer
                            updateInterval={ 1000 }
                            updateWhenZooming={ false }
                            attribution="&amp;copy Accession localities from <a href=&quot;/&quot;>Genesys PGR</a>"
                            url={ layerUrl }
186
                            subdomains={ mapInfo.tileServers }
187
                        />
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
188
189
190
191
192
193
194
195
196
197
                        { 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>
                        }
198
199
200
201
202
203
204
205
206
                    </Map>
                }
                </div>
            </PageLayout>
        );
    }
}

const mapStateToProps = (state, ownProps) => ({
207
    mapInfo: state.accessions.public.mapInfo || undefined,
208
209
210
211
212
    filterCode: ownProps.match.params.filterCode || '',
    currentTab: ownProps.match.params.tab || 'map', // current tab, or ownProps.location.pathname
});

const mapDispatchToProps = (dispatch) => bindActionCreators({
213
    loadAccessionsMapInfo,
214
215
}, dispatch);

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