Passed
Push — master ( 33d732...d15518 )
by Sébastien
01:24
created

mapDirections.route   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 3
Ratio 100 %

Importance

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