Passed
Push — master ( 7b2938...53bd9e )
by Sébastien
48s
created

map.load   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 28
Code Lines 16

Duplication

Lines 28
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
eloc 16
c 0
b 0
f 0
nc 1
nop 2
dl 28
loc 28
rs 9.6
1
/*global: $, mapboxgl, MapboxDirections, turf*/
2
3 View Code Duplication
$(window).on("scroll", function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
4
  var scrollValue = $(window).scrollTop();
5
  if (scrollValue > 220) {
6
    $(".navbar").addClass("affix");
7
  } else {
8
    $(".navbar").removeClass("affix");
9
  }
10
});
11
12
/*Load location (stores2)*/
13
function loadLocations(asynchr = true) {
14
  var locations = null;
15
  $.ajax({
16
    async: asynchr,
17
    global: false,
18
    url: "https://leipzig-einkaufen.de/location.json",
19
    //"url": "http://localhost/vectortiles/museen.json",
20
    dataType: "json",
21
    success: function (data) {
22
      locations = data;
23
    }
24
  });
25
  return locations;
26
}
27
28
var stores2 = null; // = loadLocations();
29
// Set bounds to Leipzig, Germany
30
var bounds = [
31
  [12.179, 51.227], // Southwest coordinates
32
  [12.6, 51.459] // Northeast coordinates
33
];
34
35
// declare map
36
// var map = new mapboxgl.Map({
37
//   container: "map",
38
//   style: "https://leipzig-einkaufen.de/json/style-local.json",
39
//   //style: "http://localhost/vectortiles/json/style-local.json",
40
41
//   center: [12.3722, 51.3272],
42
//   zoom: 11,
43
//   attributionControl: true,
44
//   hash: false,
45
//   maxZoom: 14.9,
46
//   maxBounds: bounds // Sets bounds as max
47
// });
48
49
mapboxgl.accessToken = 'pk.eyJ1Ijoic2hldWIiLCJhIjoiWGtobTNPNCJ9.v2JwlNSGBm_KxJUKE_WLig';
50
var map = new mapboxgl.Map({
51
  container: 'map',
52
  style: 'mapbox://styles/mapbox/light-v9',
53
54
  center: [12.3722, 51.3272],
55
  zoom: 11,
56
  attributionControl: true,
57
  hash: false,
58
  maxZoom: 14.9,
59
  maxBounds: bounds // Sets bounds as max
60
});
61
62
/*Declare MapDirections*/
63
var mapDirections = new MapboxDirections();
64
/*MapDirections Settings*/
65
mapDirections.accessToken =
66
  "pk.eyJ1Ijoic2hldWIiLCJhIjoiWGtobTNPNCJ9.v2JwlNSGBm_KxJUKE_WLig";
67
mapDirections.unit = "metric";
68
mapDirections.proximity = false; /*proximity ??*/
69
mapDirections.interactive = false;
70
mapDirections.profile = "driving"; //, "walking", "cycling";
71
// UI controls
72
mapDirections.controls = {
73
  inputs: true,
74
  instructions: false
75
};
76
77
/*Add mapDirections Controls*/
78
map.addControl(new MapboxDirections(mapDirections), "top-left");
79
80
map.addControl(
81
  new mapboxgl.ScaleControl({
82
    maxWidth: 80,
83
    unit: "metric"
84
  })
85
);
86
87
var directionControl = document.getElementsByClassName(
88
  "mapboxgl-ctrl-directions"
89
);
90
directionControl["0"].hidden = true;
91
var ptsWithin = null;
92
93
var filterEl = document.getElementById("feature-filter");
94
var listingsEl = document.getElementById("listings");
95
var txtCategoriesEl = document.getElementById("txtCategories");
96
97
// Empty Geojson Data
98
var bufferedLinestring = {
99
  id: "0",
100
  type: "Feature",
101
  geometry: {
102
    type: "Point",
103
    coordinates: [0, 0]
104
  },
105
  properties: {}
106
};
107
108
// Functions
109
function normalizeString(string) {
110
  return string.trim().toLowerCase();
111
}
112
113
function createPopUp(currentFeature) {
114
  new mapboxgl
115
    .Popup({
116
      closeOnClick: true
117
    })
118
    .setLngLat(currentFeature.geometry.coordinates)
119
    .setHTML(
120
      "<h3>" +
121
      currentFeature.properties.name +
122
      "</h3>" +
123
      "<h4>" +
124
      currentFeature.properties.description +
125
      "</h4>"
126
    )
127
    .addTo(map);
128
}
129
130
function getUniqueFeatures(array, comparatorProperty) {
131
  var existingFeatureKeys = {};
132
  // Because features come from tiled vector data, feature geometries may be split
133
  // or duplicated across tile boundaries and, as a result, features may appear
134
  // multiple times in query results.
135
  var uniqueFeatures = array.filter(function (el) {
136
    if (existingFeatureKeys[el.properties[comparatorProperty]]) {
137
      return false;
138
    }
139
    existingFeatureKeys[el.properties[comparatorProperty]] = true;
140
    return true;
141
  });
142
143
  return uniqueFeatures;
144
}
145
146
function colorLocationList(data) {
147
  // Iterate through the list of stores
148
  // WITHIN THE CALCULATED ROUTE !! and color in green
149
  if (data.length) {
150
    data.forEach(function (feature) {
151
      // Shorten data.feature.properties to just `prop`.
152
      var prop = feature.properties;
153
      var cardHeader = document.getElementById("heading" + prop.id);
154
      if (cardHeader === null) {
155
        return;
156
      }
157
158
      var cardTitle = cardHeader.getElementsByClassName("title");
159
      cardTitle[0].style.color = "#608BC7";
160
    });
161
  }
162
}
163
164
function buildLocationList(data) {
165
  // Iterate through the list of stores
166
  listingsEl.innerHTML = "";
167
  if (data.length) {
168
    data.forEach(function (feature) {
169
      // Shorten feature.properties to just `prop` so we're not writing this long form over and over again.
170
      var prop = feature.properties;
171
172
      // Select the listing container in the HTML and append a div  with the class 'item' for each store
173
      var card = listingsEl.appendChild(document.createElement("div"));
174
      card.className = "item card cardList";
175
      card.id = prop.id;
176
177
      var cardHeader = card.appendChild(document.createElement("div"));
178
      cardHeader.className = "card-header";
179
      cardHeader.setAttribute("role", "tab");
180
181
      cardHeader.setAttribute("id", "heading" + card.id);
182
      cardHeader.id = "heading" + card.id;
183
184
      var cardMb0 = cardHeader.appendChild(document.createElement("h5"));
185
      cardMb0.className = "mb-0";
186
187
      // Create a new link with the class 'title' for each store and fill it with the store address
188
      var link = cardMb0.appendChild(document.createElement("a"));
189
      link.setAttribute("data-toggle", "collapse");
190
      link.href = "#collapse" + card.id;
191
      link.setAttribute("aria-expanded", "false");
192
      link.setAttribute("aria-controls", "collapse" + card.id);
193
      link.className = "title";
194
      link.textContent = prop.name;
195
      link.title =  "Brief description of " + prop.name;
196
      link.dataPosition = card.id;
197
198
      var cardCollapse = card.appendChild(document.createElement("div"));
199
      cardCollapse.className = "collapse";
200
      cardCollapse.setAttribute("id", "collapse" + card.id);
201
      cardCollapse.setAttribute("role", "tabpanel");
202
      cardCollapse.setAttribute("aria-labelledby", "heading" + card.id);
203
      cardCollapse.setAttribute("data-parent", "#listings");
204
205
      if (prop.image) {
206
        var cardImg = cardCollapse.appendChild(document.createElement("img"));
207
        cardImg.className = "img-responsive img-listing lazyload";
208
        cardImg.src = prop.image;
209
        cardImg.alt = prop.name;
210
        cardImg.title = prop.name;
211
      }
212
213
      var cardBody = cardCollapse.appendChild(document.createElement("div"));
214
      cardBody.className = "card-body";
215
      cardBody.textContent = prop.description;
216
      cardBody.appendChild(document.createElement("br"));
217
218
      if (prop.url) {
219
        var linkBody = cardBody.appendChild(document.createElement("a"));
220
        linkBody.textContent = prop.name;
221
        linkBody.href = prop.url;
222
        linkBody.target = "_blank";
223
        linkBody.title = prop.name;
224
        linkBody.rel = "noopener";
225
      }
226
227
      // Add an event listener for the links in the sidebar listing
228
      link.addEventListener("click", function () {
229
        // Update the currentFeature to the store associated with the clicked link
230
        var clickedListing = stores2.features[this.dataPosition];
231
232
        var popUps = document.getElementsByClassName("mapboxgl-popup");
233
        // Check if there is already a popup on the map and if so, remove it
234
        if (popUps[0]) {
235
          popUps[0].parentNode.removeChild(popUps[0]);
236
        }
237
238
        // 1. Close all other popups and display popup for clicked store
239
        createPopUp(clickedListing);
240
241
        // 2. Highlight listing in sidebar (and remove highlight for all other listings)
242
        var activeItem = document.getElementsByClassName("is-active");
243
        if (activeItem[0]) {
244
          activeItem[0].classList.remove("is-active");
245
        }
246
        this.classList.add("is-active");
247
      });
248
    });
249
  } else {
250
    var empty = document.createElement("p");
251
    empty.textContent = "Ziehen Sie die Karte, um die Ergebnisse zu füllen";
252
    listingsEl.appendChild(empty);
253
254
    // remove features filter
255
    map.setFilter("locations", ["has", "Categories"]);
256
  }
257
258
  // Populate features for the listing overlay.
259
  if (ptsWithin) {
260
    colorLocationList(ptsWithin.features);
261
  }
262
}
263
264
function filterOnRoute() {
265
  var mapDirectionsSource = map.getSource("directions");
266
  var radius = 0.6;
267
  var unit = "kilometers";
268
269
  //var distDuration = mapDirections.getDistanceAndDuration();
270
271
  // buffer the route with a area of radius 'radius'
272
  if (mapDirectionsSource._data.features.length < 2) {
273
    return;
274
  }
275
  var bufferedLinestring = turf.buffer(
276
    mapDirectionsSource._data.features[2].geometry,
277
    radius, {
278
      units: unit
279
    }
280
  );
281
282
  // update bufferedTraceSource
283
  map.getSource("bufferedTraceSource").setData(bufferedLinestring);
284
285
  // Get locations rendered on the map
286
  var features = map.queryRenderedFeatures({
287
    layers: ["locations"]
288
  });
289
290
  // use featureCollection to convert features (array of features) into a collection of features (Object type FeatureCollection);
291
  var collection = turf.featureCollection(features);
292
293
  // Filter the points to the area around the direction
294
  ptsWithin = turf.pointsWithinPolygon(collection, bufferedLinestring);
295
296
  // Populate features for the listing overlay.
297
  if (ptsWithin) {
298
    buildLocationList(features);
299
  }
300
}
301
302
function displayDirectionControls() {
303
  var directionControl = document.getElementsByClassName(
304
    "mapboxgl-ctrl-directions"
305
  );
306
  if (directionControl["0"].hidden) {
307
    directionControl["0"].hidden = false;
308
    map.setLayoutProperty("bufferedTraceLayer", "visibility", "visible");
309
310
    map.setLayoutProperty("directions-origin-point", "visibility", "visible");
311
    map.setLayoutProperty(
312
      "directions-destination-point",
313
      "visibility",
314
      "visible"
315
    );
316
    map.setLayoutProperty("directions-origin-label", "visibility", "visible");
317
    map.setLayoutProperty(
318
      "directions-destination-label",
319
      "visibility",
320
      "visible"
321
    );
322
323
    map.setLayoutProperty("directions-hover-point", "visibility", "visible");
324
    map.setLayoutProperty("directions-waypoint-point", "visibility", "visible");
325
    map.setLayoutProperty("directions-route-line", "visibility", "visible");
326
    map.setLayoutProperty("directions-route-line-alt", "visibility", "visible");
327
    filterOnRoute();
328
  } else {
329
    directionControl["0"].hidden = true;
330
    // reinitialize ptsWithin
331
    ptsWithin = null;
332
333
    map.setLayoutProperty("bufferedTraceLayer", "visibility", "none");
334
    map.setLayoutProperty("directions-origin-point", "visibility", "none");
335
    map.setLayoutProperty("directions-destination-point", "visibility", "none");
336
    map.setLayoutProperty("directions-origin-label", "visibility", "none");
337
    map.setLayoutProperty("directions-destination-label", "visibility", "none");
338
339
    map.setLayoutProperty("directions-hover-point", "visibility", "none");
340
    map.setLayoutProperty("directions-waypoint-point", "visibility", "none");
341
    map.setLayoutProperty("directions-route-line", "visibility", "none");
342
    map.setLayoutProperty("directions-route-line-alt", "visibility", "none");
343
344
    var features = map.queryRenderedFeatures({
345
      layers: ["locations"]
346
    });
347
348
    if (features) {
349
      // Populate features for the listing overlay.
350
      buildLocationList(features);
351
    }
352
  }
353
}
354
355
// Call buildlist function on initialization
356
if (!stores2) {
357
  stores2 = loadLocations(false);
358
  buildLocationList(stores2.features);
359
}
360
361
362
// Load map
363
map.on("load", function (e) {
364
365
  map.loadImage(
366
    "https://leipzig-einkaufen.de/media/diagonal-noise.png",
367
    function (error, image) {
368
      if (error) {
369
        throw error;
370
      }
371
      map.addImage("background_pattern", image);
372
    }
373
  );
374
375
  map.loadImage(
376
    "https://leipzig-einkaufen.de/media/Marker_with_Shadow.png",
377
    function (error, image) {
378
      if (error) {
379
        throw error;
380
      }
381
      map.addImage("marker_z", image);
382
383
384
385
      // Add the stores2 (locations_source) to the map
386
      map.addSource("locations_source", {
387
        type: "geojson",
388
        data: "https://leipzig-einkaufen.de/location.json"
389
      });
390
391
      // Add the locations_source to the map as a layer
392
      map.addLayer({
393
        id: "locations",
394
        type: "symbol",
395
        // Add a GeoJSON source containing place coordinates and information.
396
        source: "locations_source",
397
        layout: {
398
          visibility: "visible",
399
          "icon-image": "marker_z",
400
          "icon-size": 0.9,
401
          "icon-allow-overlap": true
402
        }
403
      });
404
    });
405
406
  // Add the bufferedLinestring to the map as a layer
407
  map.addSource("bufferedTraceSource", {
408
    type: "geojson",
409
    data: bufferedLinestring,
410
    maxzoom: 13
411
  });
412
413
  map.addLayer({
414
    id: "bufferedTraceLayer",
415
    type: "fill",
416
    source: "bufferedTraceSource",
417
    layout: {
418
      visibility: "visible"
419
    },
420
    paint: {
421
      "fill-color": "rgb(0,0,0)",
422
      "fill-opacity": 1,
423
      "fill-translate": [0, 2.5],
424
      "fill-pattern": "background_pattern"
425
    }
426
  });
427
428
  // Add Fullscreen control to the map.
429
  map.addControl(new mapboxgl.FullscreenControl());
430
431
  // Add geolocate control to the map.
432
  map.addControl(
433
    new mapboxgl.GeolocateControl({
434
      positionOptions: {
435
        enableHighAccuracy: true
436
      },
437
      trackUserLocation: true
438
    })
439
  );
440
441
  // When a click event occurs on a feature in the places layer, open a popup at the
442
  // location of the feature, with description HTML from its properties.
443
  map.on("click", "locations", function (e) {
444
    var currentFeature = e.features[0];
445
    // 1. Create Popup
446
    createPopUp(currentFeature);
447
448
    // 2. Highlight listing in sidebar (and remove highlight for other listing)
449
    var activeItem = document.getElementsByClassName("is-active");
450
    if (activeItem[0]) {
451
      activeItem[0].classList.remove("is-active");
452
    }
453
454
    var headingElement = document.getElementById(
455
      "heading" + currentFeature.properties.id
456
    );
457
    if (headingElement) {
458
      headingElement.classList.add("is-active");
459
    }
460
    var collapseElement = document.getElementById(
461
      "collapse" + currentFeature.properties.id
462
    );
463
    if (collapseElement) {
464
      $(collapseElement).collapse("show");
465
    }
466
  });
467
468
  map.on("moveend", function () {
469
    // Query all the rendered points in the view
470
    var features = map.queryRenderedFeatures({
471
      layers: ["locations"]
472
    });
473
474
    if (features) {
475
      //var uniqueFeatures = getUniqueFeatures(features, "Categories");
476
477
      // Populate features for the listing overlay.
478
      buildLocationList(features);
479
480
      // Clear the input container
481
      filterEl.value = "";
482
483
      // Store the current features in sn `locations_on_map` variable to later use for filtering on `keyup`.
484
      //locations = features;
485
    }
486
  });
487
488
  map.on("mousemove", "locations", function () {
489
    // Change the cursor style as a UI indicator.
490
    map.getCanvas().style.cursor = "pointer";
491
  });
492
493
  map.on("mouseleave", "locations", function () {
494
    map.getCanvas().style.cursor = "";
495
  });
496
497
  $(".dropdown-item").click(function () {
498
499
    var value = normalizeString($(this).text());
500
    var filtered = map.querySourceFeatures("locations_source");
501
502
    if (value !== "alle") {
503
      // Filter visible features that don't match the input value.
504
      filtered = filtered.filter(function (feature) {
505
        var name = normalizeString(feature.properties.name);
506
        var Categories = normalizeString(feature.properties.Categories);
507
        return name.indexOf(value) > -1 || Categories.indexOf(value) > -1;
508
      });
509
    }
510
    if (!filtered) {
511
      return;
512
    }
513
514
    var uniqueFeatures = getUniqueFeatures(filtered, "Categories");
515
    // Populate the sidebar with filtered results
516
    buildLocationList(uniqueFeatures);
517
518
    // Set the filter to populate features into the layer.
519
    map.setFilter(
520
      "locations", ["in", "name"].concat(
521
        uniqueFeatures.map(function (feature) {
522
          return feature.properties.name;
523
        })
524
      )
525
    );
526
527
    txtCategoriesEl.value = value;
528
  });
529
});
530
531
// Direction event listener
532
mapDirections.on("route", function () {
533
  filterOnRoute();
534
});
535
536
// Display Direction
537
$("#btnDisplayControls").on("click", function () {
538
  displayDirectionControls();
539
});