Skip to content

Commit 184d439

Browse files
authored
Merge pull request #142 from carlnewton/117-current-location
Add button to show current location
2 parents 1eff87c + 179eeeb commit 184d439

16 files changed

Lines changed: 562 additions & 332 deletions

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- [#123] Dark mode
13+
- [#117] Users can see their location on the post map
1314
- [#131] A message displays when an anonymous user clicks a heart
1415

16+
### Changed
17+
18+
- Map marker icon inherits the style of the Habitat logo
19+
1520
### Fixed
1621

1722
- Do not expect only database ID to be 1 for any entity

assets/create_post.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ map.setMaxBounds(perimeter.getBounds().pad(0.3));
3030
map.setMinZoom(map.getBoundsZoom(map.options.maxBounds));
3131

3232
var markerIcon = L.icon({
33-
iconUrl: '/build/images/marker-icon.2b3e1faf.png',
34-
iconSize: [25, 41],
35-
iconAnchor: [13, 41],
33+
iconUrl: '/build/images/marker_icon.png',
34+
iconSize: [30, 41],
35+
iconAnchor: [15, 41],
3636
})
3737

3838
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {

assets/img/location_icon.png

922 Bytes
Loading

assets/img/marker_icon.png

1.17 KB
Loading

assets/styles/app.scss

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,48 @@ body {
1414
}
1515

1616
#map { height: 100%; }
17+
18+
#map .leaflet-btn {
19+
background-color: #fff;
20+
border: 2px solid rgba(0,0,0,0.2);
21+
border-radius: 4px;
22+
background-clip: padding-box;
23+
font-size: 15px;
24+
height: 33px;
25+
width: 33px;
26+
}
27+
28+
#map .leaflet-btn-active {
29+
background-color: #fffacd;
30+
}
31+
32+
#map .leaflet-btn:hover {
33+
background: rgb(244, 244, 244);
34+
border: 2px solid rgba(0,0,0,0.33);
35+
}
36+
37+
#map .leaflet-btn-active:hover {
38+
background-color: #ffffe0;
39+
}
40+
41+
@keyframes pulse {
42+
0% { filter: brightness(1); }
43+
50% { filter: brightness(1.3); }
44+
100% { filter: brightness(1); }
45+
}
46+
#map .my-location-icon {
47+
animation: pulse 2s infinite;
48+
}
49+
50+
@keyframes walk {
51+
to {
52+
stroke-dashoffset: 24;
53+
}
54+
}
55+
#map .location-path {
56+
animation: walk 1s infinite;
57+
}
58+
1759
.post-card a {
1860
text-decoration: none !important;
1961
}

assets/view_map.js

Lines changed: 156 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ require('leaflet');
22

33
if (document.getElementById('map') !== null) {
44
var location;
5+
var watchPosition;
6+
var myLocationEnabled = false;
7+
var myLocationRefresh = false;
8+
var relativeDistanceControl;
59
var centerLatLng = document.getElementById('map').dataset.center;
610
var centerLatLngArr = [
711
centerLatLng.substring(0, centerLatLng.indexOf(',')),
@@ -13,16 +17,39 @@ if (document.getElementById('map') !== null) {
1317
});
1418

1519
var markerIcon = L.icon({
16-
iconUrl: '/build/images/marker-icon.2b3e1faf.png',
17-
iconSize: [25, 41],
18-
iconAnchor: [13, 41],
20+
iconUrl: '/build/images/marker_icon.png',
21+
iconSize: [30, 41],
22+
iconAnchor: [15, 41],
1923
})
2024

2125
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
2226
maxZoom: 19,
2327
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
2428
}).addTo(map);
2529

30+
L.Control.MyLocation = L.Control.extend({
31+
onAdd: function(map) {
32+
viewLocationButton = L.DomUtil.create('button', 'leaflet-btn');
33+
34+
viewLocationButton.id = 'view-location-btn';
35+
viewLocationButton.innerHTML = '<i class="bi bi-compass"></i>';
36+
viewLocationButton.title = document.getElementById('map').dataset.showcurrentlocation;
37+
viewLocationButton.setAttribute('data-test', 'current-location-btn')
38+
39+
return viewLocationButton;
40+
},
41+
42+
onRemove: function(map) {
43+
}
44+
})
45+
L.control.MyLocation = function(opts) {
46+
return new L.Control.MyLocation(opts);
47+
}
48+
49+
L.control.MyLocation({
50+
position: 'bottomleft'
51+
}).addTo(map);
52+
2653
let latLng = L.latLng(
2754
centerLatLngArr
2855
);
@@ -31,4 +58,130 @@ if (document.getElementById('map') !== null) {
3158

3259
map.addLayer(location);
3360
map.setMaxBounds(latLng.toBounds(500));
61+
62+
document.getElementById('view-location-btn').onclick = function() {
63+
myLocationEnabled = !myLocationEnabled;
64+
if (myLocationEnabled) {
65+
viewLocationButton.title = document.getElementById('map').dataset.hidecurrentlocation;
66+
document.getElementById('view-location-btn').classList.add('leaflet-btn-active');
67+
watchPosition = navigator.geolocation.watchPosition(setCurrentPosition, failCurrentPosition);
68+
} else {
69+
navigator.geolocation.clearWatch(watchPosition);
70+
disableCurrentLocation();
71+
}
72+
}
73+
74+
function centerOnBounds(bounds) {
75+
map.panTo(bounds.getCenter());
76+
map.setMaxBounds(bounds.pad(0.5));
77+
map.fitBounds(bounds);
78+
}
79+
80+
function centerOnMarker() {
81+
map.removeLayer(myLocation);
82+
map.removeLayer(line);
83+
map.setMaxBounds(latLng.toBounds(500));
84+
map.panTo(latLng);
85+
}
86+
87+
function failCurrentPosition(err) {
88+
switch (err.code) {
89+
case 1:
90+
alert(document.getElementById('map').dataset.error);
91+
break;
92+
default:
93+
alert(err.message);
94+
}
95+
96+
disableCurrentLocation();
97+
}
98+
99+
function disableCurrentLocation() {
100+
viewLocationButton.title = document.getElementById('map').dataset.showcurrentlocation;
101+
document.getElementById('view-location-btn').classList.remove('leaflet-btn-active');
102+
103+
if (relativeDistanceControl !== undefined) {
104+
relativeDistanceControl.remove();
105+
relativeDistanceControl = undefined;
106+
}
107+
myLocationRefresh = false;
108+
centerOnMarker();
109+
}
110+
111+
function setCurrentPosition(position) {
112+
if (!myLocationEnabled) {
113+
return;
114+
}
115+
116+
var currentLatLng = L.latLng(parseFloat(position.coords.latitude).toPrecision(6), parseFloat(position.coords.longitude).toPrecision(6));
117+
118+
var locationIcon = L.icon({
119+
iconUrl: '/build/images/location_icon.png',
120+
iconSize: [30, 30],
121+
iconAnchor: [15, 15],
122+
className: 'my-location-icon',
123+
})
124+
125+
126+
let bounds = L.latLngBounds(latLng, currentLatLng);
127+
if (myLocationRefresh) {
128+
myLocation.setLatLng(currentLatLng);
129+
map.removeLayer(line);
130+
line = undefined;
131+
distanceIndicator.innerHTML = printDistance(map.distance(currentLatLng, latLng));
132+
// TODO:
133+
// When the user is moving, it would be nice to center on the bounds of the target and their location
134+
// but only if they haven't panned or zoomed - because otherwise that would be a frustrating experience.
135+
// This is difficult because leaflet cannot currently discern between automated and manual pan/zoom events.
136+
// Once this is available, we can implement it: https://github.com/Leaflet/Leaflet/pull/6929
137+
// centerOnBounds(bounds);
138+
} else {
139+
myLocation = L.marker(currentLatLng, { icon: locationIcon, zIndexOffset: -1000 });
140+
map.addLayer(myLocation);
141+
myLocationRefresh = true;
142+
centerOnBounds(bounds);
143+
144+
L.Control.RelativeDistance = L.Control.extend({
145+
onAdd: function(map) {
146+
distanceIndicator = L.DomUtil.create('div', 'bg-light rounded shadow-sm opacity-75 p-1 fs-6');
147+
148+
distanceIndicator.id = 'distanceIndicator';
149+
distanceIndicator.title = document.getElementById('map').dataset.showcurrentlocation;
150+
distanceIndicator.innerHTML = printDistance(map.distance(currentLatLng, latLng));
151+
distanceIndicator.setAttribute('data-test', 'distance-indicator')
152+
153+
return distanceIndicator;
154+
},
155+
156+
onRemove: function(map) {
157+
}
158+
})
159+
L.control.RelativeDistance = function(opts) {
160+
return new L.Control.RelativeDistance(opts);
161+
}
162+
163+
relativeDistanceControl = L.control.RelativeDistance({
164+
position: 'topright'
165+
}).addTo(map);
166+
}
167+
168+
line = L.polyline([latLng, currentLatLng], { color: 'white', weight: 6, dashArray: '10,14', className: 'location-path' });
169+
line.addTo(map);
170+
}
171+
172+
function printDistance(meters) {
173+
let distance;
174+
if (document.getElementById('map').dataset.measurementtype === 'km') {
175+
distance = parseFloat(meters / 1000).toFixed(1);
176+
} else {
177+
distance = parseFloat(meters * 0.000621371192).toFixed(1);
178+
}
179+
180+
if (distance % 1 === 0) {
181+
distance = Math.round(distance);
182+
}
183+
184+
return distance + ' ' + document.getElementById('map').dataset.measurement + ' ' + document.getElementById('map').dataset.away;
185+
}
34186
}
187+

composer.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@
77
"php": ">=8.4.17",
88
"ext-ctype": "*",
99
"ext-iconv": "*",
10-
"aws/aws-sdk-php": "^3.380.3",
10+
"aws/aws-sdk-php": "^3.383.1",
1111
"doctrine/doctrine-bundle": "^3.2.2",
1212
"doctrine/doctrine-migrations-bundle": "^4.0",
13-
"doctrine/orm": "^3.6.4",
13+
"doctrine/orm": "^3.6.7",
1414
"dragonmantank/cron-expression": ">=3.6",
1515
"knplabs/knp-time-bundle": "^2.5",
1616
"symfony/console": "8.0.*",
1717
"symfony/dotenv": "8.0.*",
1818
"symfony/expression-language": "8.0.*",
19-
"symfony/flex": "^2.10",
19+
"symfony/flex": "^2.11",
2020
"symfony/framework-bundle": "8.0.*",
2121
"symfony/html-sanitizer": "8.0.*",
2222
"symfony/mailer": "8.0.*",
@@ -29,7 +29,7 @@
2929
"symfony/security-csrf": "8.0.*",
3030
"symfony/translation": "8.0.*",
3131
"symfony/twig-bundle": "8.0.*",
32-
"symfony/ux-twig-component": "^2.35",
32+
"symfony/ux-twig-component": "^2.36",
3333
"symfony/validator": "8.0.*",
3434
"symfony/webpack-encore-bundle": "^2.4",
3535
"symfony/yaml": "8.0.*"
@@ -89,8 +89,8 @@
8989
},
9090
"require-dev": {
9191
"doctrine/doctrine-fixtures-bundle": "^4.3.1",
92-
"friendsofphp/php-cs-fixer": "^3.95.1",
93-
"phpunit/phpunit": "^13.1.8",
92+
"friendsofphp/php-cs-fixer": "^3.95.3",
93+
"phpunit/phpunit": "^13.1.13",
9494
"symfony/browser-kit": "8.0.*",
9595
"symfony/css-selector": "8.0.*",
9696
"symfony/maker-bundle": "^1.67.0"

0 commit comments

Comments
 (0)