MapPage.tsx 9.26 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';
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
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';
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
17
18
import ContentLayout from 'ui/layout/ContentLayout';
import MapConfigSection from './c/MapConfigSection';
19

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
20
import AccessionService from 'service/genesys/AccessionService';
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
21
import PageTitle from 'ui/common/PageTitle';
22
23
24

let Map;
let TileLayer;
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
25
let Popup;
Oleksii Savran's avatar
Oleksii Savran committed
26
let Rectangle;
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
27
28

const popupContentLimit = 11;
29

30
31
32
33
interface IMapPageProps {
    classes?: any;
    t?: any;

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

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
51
52
53

    private clickTimeout;

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

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
60
    public state = {
Oleksii Savran's avatar
Oleksii Savran committed
61
62
      clickLocation: [],
      searchBox: null,
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
63
64
65
66
      geoData: [],
      otherCount: 0,
    };

67
68
69
70
71
    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
72
            Popup = require('react-leaflet').Popup;
Oleksii Savran's avatar
Oleksii Savran committed
73
            Rectangle = require('react-leaflet').Rectangle;
74
75
76
77
78
        }
    }


    public componentWillMount() {
79
80
81
82
83
84
85
      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 || '');
86
87
88
      }
    }

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

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

Oleksii Savran's avatar
Oleksii Savran committed
111
112
113
114
115
116
            const width = Math.pow(2, currentZoom - 2);
            console.log(`zoom=${currentZoom} width=${width} diff=${ 3 / width}`);
            const searchBounds: number[][] = [
                [ e.latlng.lat - (3 / width), correctLng - (3 / width) ],
                [ e.latlng.lat + (3 / width), correctLng + (3 / width) ],
            ];
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
117
118
119
120
            const filterWithGeo = {
                ...filter,
                geo: {
                    longitude: {
Oleksii Savran's avatar
Oleksii Savran committed
121
122
                        ge: searchBounds[0][1],
                        le: searchBounds[1][1],
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
123
124
                    },
                    latitude: {
Oleksii Savran's avatar
Oleksii Savran committed
125
126
                        ge: searchBounds[0][0],
                        le: searchBounds[1][0],
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
127
128
129
130
                    },
                },
            };
            AccessionService.geoJson(filterWithGeo, popupContentLimit)
Oleksii Savran's avatar
Oleksii Savran committed
131
                .then((res) => this.setState({clickLocation: [ e.latlng.lat,  e.latlng.lng], searchBox: searchBounds, geoData: res.geoJson, otherCount: res.otherCount}));
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
132
133
134
        }, 200);
    }

135
136
137
138
139
    /// Wrap loadAccessionsMapInfo dispatch and fills the current sort selection
    protected myApplyFilters = (filters: AccessionFilter) => {
      const { loadAccessionsMapInfo } = this.props;

      loadAccessionsMapInfo(filters);
140
141
142
    }

    public render() {
Oleksii Savran's avatar
Oleksii Savran committed
143
        const { searchBox, geoData, otherCount } = this.state;
144
145
        const position = [30, 0];

Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
146
        const { mapInfo, mapLayers, currentTab, classes, filterCode, t } = this.props;
147
148
149
150

        if (! mapInfo) {
          return <Loading />;
        }
151
152

        // const color = 'f00ba0';
153
        const layerUrl = `{s}/acn/tile/{z}/{x}/{y}?f=${filterCode ? filterCode : ''}`; // `&color=${color}`;
154
155

        return (
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
156
157
158
159
160
            <ContentLayout
              right={ <MapConfigSection/> }
              rightAlwaysCollapsible
              customHeaderHeight
            >
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
161
162
163
              <PageTitle title={ t('accessions.public.p.browse.title') } />
              <ContentHeader title={ t('accessions.public.p.browse.title') } subTitle={ t('accessions.public.p.browse.subTitle') } />
              <Tabs
164
165
                    tab={ currentTab }
                    actions={
Oleksii Savran's avatar
Oleksii Savran committed
166
                      <ButtonBar>
167
                        <span>
Matija Obreza's avatar
Matija Obreza committed
168
169
                          <form method="post" action="/proxy/api/v1/acn/downloadKml">
                            <input type="hidden" name="f" value={ filterCode } />
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
170
                            <Button type="submit">{ `${t('common:action.download')} ${t('accessions.public.p.map.kml')}` }</Button>
Matija Obreza's avatar
Matija Obreza committed
171
                          </form>
172
                        </span>
Oleksii Savran's avatar
Oleksii Savran committed
173
                      </ButtonBar>
174
175
                    }
                >
Viacheslav Pavlov's avatar
i18n    
Viacheslav Pavlov committed
176
177
178
                  <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>
179
180
181
                </Tabs>
                <PrettyFilters
                    prefix="accessions"
182
                    filterObj={ mapInfo && mapInfo.filter || {} }
183
184
185
                    onSubmit={ this.myApplyFilters }
                />
                <div className={ classes.leafletContainer }>
186
                { mapInfo && typeof window !== 'undefined' &&
187
                    <Map
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
188
                        onClick={ this.onMapClick }
189
                        center={ position }
190
191
                        zoom={ 3 } minZoom={ 2 } maxZoom={ 14 }
                        bounds={ mapInfo.bounds }>
192
                        <TileLayer
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
193
                            zIndex={ 0 }
194
195
196
197
                            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
198
199
200

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

Matija Obreza's avatar
Matija Obreza committed
201
                        <TileLayer
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
202
                            zIndex={ mapLayers.length + 1 }
203
204
205
206
                            updateInterval={ 1000 }
                            updateWhenZooming={ false }
                            attribution="&amp;copy Accession localities from <a href=&quot;/&quot;>Genesys PGR</a>"
                            url={ layerUrl }
207
                            subdomains={ mapInfo.tileServers }
208
                        />
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
209
                        { geoData && geoData.length > 0 &&
Oleksii Savran's avatar
Oleksii Savran committed
210
211
212
213
                            <Rectangle
                              bounds={ searchBox }
                              ref={ (marker) => marker && marker.leafletElement.openPopup() }
                            >
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
214
215
                                <Popup open>
                                    <div>
216
                                        { geoData.map((feature, idx) => (<div key={ idx }><Link to={ `/a/${feature.properties.uuid}` }>{ `${feature.properties.accessionNumber} ${feature.properties.instCode}` }</Link></div>)) }
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
217
218
219
                                        { otherCount > 0 && <div>{ t('accessions.public.p.map.andMore', {otherMore: otherCount}) }</div> }
                                    </div>
                                </Popup>
Oleksii Savran's avatar
Oleksii Savran committed
220
                            </Rectangle>
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
221
                        }
222
223
224
                    </Map>
                }
                </div>
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
225
            </ContentLayout>
226
227
228
229
230
        );
    }
}

const mapStateToProps = (state, ownProps) => ({
231
    mapInfo: state.accessions.public.mapInfo || undefined,
Viacheslav Pavlov's avatar
Viacheslav Pavlov committed
232
    mapLayers: state.accessions.public.mapLayers,
233
234
235
236
237
    filterCode: ownProps.match.params.filterCode || '',
    currentTab: ownProps.match.params.tab || 'map', // current tab, or ownProps.location.pathname
});

const mapDispatchToProps = (dispatch) => bindActionCreators({
238
    loadAccessionsMapInfo,
239
240
}, dispatch);

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