Completed
Push — master ( 1ab771...ef861a )
by Matt
32s queued 12s
created

assets/map.js   A

Complexity

Total Complexity 21
Complexity/F 1.31

Size

Lines of Code 208
Function Count 16

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 129
c 0
b 0
f 0
dl 0
loc 208
rs 10
wmc 21
mnd 5
bc 5
fnc 16
bpm 0.3125
cpm 1.3125
noi 0
1
// TODO: Refactor the heck out of this mess
2
3
/* LeafletJS */
4
/** global: L */
5
/* global L */ /* For ESLint */
6
7
import 'leaflet/dist/leaflet.css';
8
import 'leaflet-loading/src/Control.Loading.css';
9
import 'leaflet.locatecontrol/dist/L.Control.Locate.css';
10
import './styles/Leaflet.Photo.css';
11
12
const $ = require('jquery');
13
14
require('leaflet');
15
require('leaflet.locatecontrol');
16
require('leaflet-loading');
17
require('leaflet.markercluster');
18
require('polyline-encoded'); // Adds support for Google polyline encoding https://www.npmjs.com/package/polyline-encoded
19
require('./Leaflet.Photo');
20
21
let streetMap;
22
let satelliteMap;
23
24
const base = L.latLng($('#mapid').data('homebaseLat'), $('#mapid').data('homebaseLng'));
25
26
export function setUpMap(options) {
27
  const mapboxAccessToken = $('#mapid').data('mapboxAccessToken');
28
  const locusRadius = 1609.34; // 1 mile
29
30
  streetMap = L.tileLayer(
31
    `https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}@2x?access_token=${mapboxAccessToken}`,
32
    {
33
      // These are mapbox-specific
34
      id: 'gothick/ckhb31na304g619t67r3gcngx',
35
      tileSize: 512,
36
      zoomOffset: -1,
37
      // More general
38
      maxZoom: 19,
39
      attribution: "Map data &copy; <a href='https://www.openstreetmap.org/'>OpenStreetMap</a> contributors, <a href='https://creativecommons.org/licenses/by-sa/2.0/'>CC-BY-SA</a>, Imagery © <a href='https://www.mapbox.com/'>Mapbox</a>",
40
    },
41
  );
42
43
  satelliteMap = L.tileLayer(
44
    `https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}@2x?access_token=${mapboxAccessToken}`,
45
    {
46
      // These are mapbox-specific
47
      id: 'gothick/ckhwgr59r0uai19o077hp87w4',
48
      tileSize: 512,
49
      zoomOffset: -1,
50
      // More general
51
      maxZoom: 19,
52
      attribution: "Map data &copy; <a href='https://www.openstreetmap.org/'>OpenStreetMap</a> contributors, <a href='https://creativecommons.org/licenses/by-sa/2.0/'>CC-BY-SA</a>, Imagery © <a href='https://www.mapbox.com/'>Mapbox</a>",
53
    },
54
  );
55
56
  const circle = L.circle(base, {
57
    color: 'green',
58
    fillColor: '#faa',
59
    fillOpacity: 0.15,
60
    radius: locusRadius,
61
    interactive: false,
62
  });
63
64
  // Because Object.assign isn't supported in older browsers
65
  // TODO You can go back to Object.assign now you've started using Babel in
66
  // Webpack. It'll translate for you.
67
  // https://stackoverflow.com/a/41455739/300836
68
  $.extend(options, {
69
    // Give a bit of wiggle room around the circle, but don"t let the user drift too far away
70
    maxBounds: base.toBounds(locusRadius * 5),
71
    layers: [streetMap, circle],
72
    loadingControl: true, // https://github.com/ebrelsford/Leaflet.loading
73
    tap: false, // https://github.com/domoritz/leaflet-locatecontrol#safari-does-not-work-with-leaflet-171
74
  });
75
76
  const map = L.map('mapid', options)
77
    .setView(base, 14);
78
79
  const baseMaps = {
80
    Satellite: satelliteMap,
81
    Streets: streetMap,
82
  };
83
84
  L.control.layers(baseMaps).addTo(map);
85
  L.control.scale().addTo(map);
86
87
  L.control.locate().addTo(map);
88
89
  return map;
90
}
91
92
function selectedWanderStyle() {
93
  return {
94
    weight: 5,
95
    color: '#FFA500',
96
  };
97
}
98
99
function unselectedWanderStyle() {
100
  return {
101
    weight: 4,
102
    color: '#3388FF',
103
  };
104
}
105
106
let currentlySelected = null;
107
108
let photoLayer = null;
109
110
export function addPhotos(map, photos) {
111
  if (photoLayer) {
112
    map.removeLayer(photoLayer);
113
  }
114
115
  photoLayer = L.photo.cluster().on('click', (evt) => {
116
    const { photo } = evt.layer;
117
    const template = "<a href='{imageShowUri}'><img src='{url}' width='300' /></a><p>{caption}</p>";
118
    // TODO: Video
119
    evt.layer.bindPopup(L.Util.template(template, photo), {
120
      className: 'leaflet-popup-photo',
121
      minWidth: 300,
122
    }).openPopup();
123
  });
124
125
  photoLayer.add(photos).addTo(map);
126
}
127
128
function addWanderImages(map, wanderId) {
129
  const photos = [];
130
131
  // Our API allows us to grab only those photos with co-ordinates set
132
  $.getJSON(`/api/wanders/${wanderId}/images?exists[latlng]=true`, (images) => {
133
    $.each(images['hydra:member'], (key, image) => {
134
      photos.push({
135
        lat: image.latlng[0],
136
        lng: image.latlng[1],
137
        url: image.mediumImageUri,
138
        caption: image.title || '',
139
        thumbnail: image.markerImageUri,
140
        imageShowUri: image.imageShowUri,
141
        // TODO?
142
        video: null,
143
      });
144
    });
145
    addPhotos(map, photos);
146
  });
147
}
148
149
function addWanders(url, map) {
150
  map.fireEvent('dataloading'); // Triggers loading spinner
151
  // TODO: We should probably use some kind of Hydra client. This"ll do for now.
152
  $.getJSON(url, (data) => {
153
    const nextPage = (data['hydra:view'] || {})['hydra:next'];
154
    const isLastPage = (typeof nextPage) === 'undefined';
155
    const last = data['hydra:member'].length - 1;
156
    $.each(data['hydra:member'], (key, wander) => {
157
      const isLastWander = isLastPage && (last === key);
158
      const wanderLine = L.Polyline.fromEncoded(wander.googlePolyline, {
159
        wanderId: wander.id,
160
      });
161
162
      wanderLine.setStyle(isLastWander ? selectedWanderStyle() : unselectedWanderStyle());
163
      wanderLine.bindPopup((layer) => {
164
        currentlySelected.setStyle(unselectedWanderStyle());
165
        layer.setStyle(selectedWanderStyle());
166
        layer.bringToFront();
167
        currentlySelected = layer;
168
        addWanderImages(map, layer.options.wanderId);
169
        // Popup
170
        const template = "<a href='{contentUrl}'>{title}</a>";
171
        return L.Util.template(template, wander);
172
      });
173
      if (isLastWander) {
174
        currentlySelected = wanderLine;
175
      }
176
      wanderLine.addTo(map);
177
    });
178
179
    // Recurse through all the pages in the pagination we got back.
180
    map.fireEvent('dataload');
181
    if (!isLastPage) {
182
      addWanders(nextPage, map);
183
    }
184
  });
185
}
186
187
export function addAllWanders(map) {
188
  addWanders('/api/wanders', map);
189
}
190
191
export function addWander(map, wanderId, addImages) {
192
  $.getJSON(`/api/wanders/${wanderId}`, (wander) => {
193
    const wanderLine = L.Polyline.fromEncoded(wander.googlePolyline, {
194
      wanderId: wander.id,
195
    });
196
    wanderLine
197
      .bindPopup((/* layer */) => wander.title)
198
      .addTo(map);
199
    map.fitBounds(wanderLine.getBounds());
200
    // Based on the example at https://github.com/turban/Leaflet.Photo/blob/gh-pages/examples/picasa.html
201
    if (addImages) {
202
      addWanderImages(map, wanderId);
203
    }
204
  });
205
}
206