-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmap.html
More file actions
349 lines (312 loc) · 19.5 KB
/
map.html
File metadata and controls
349 lines (312 loc) · 19.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
<!DOCTYPE html>
<html lang="en">
<head>
<title>Accessible Map Example</title>
<link href="https://fonts.googleapis.com/css?family=Arvo" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js?skin=sunburst"></script>
<script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js"></script>
<script src="https://unpkg.com/@google/markerclustererplus@4.0.1/dist/markerclustererplus.min.js"></script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDc6qMBFXVQ8WYL2tBRO6lme5dMF00i8Hc&callback=initMap"></script>
<style>
html, body {
min-height: 100%;
margin: 0;
padding: 0;
}
body {
background-color: #E2E2E2;
margin: 0;
font-family: 'Arvo', serif;
}
#wrapper {
margin: 0 auto;
width: 65%;
height: 100%;
background-color: #FFFFFF;
}
.headingWrapper {
width: 100%;
text-align: center;
margin: 0;
}
h1 {
margin: 0;
padding-top: 10px;
font-size: 40px;
}
p {
margin-left: 2%;
margin-right: 2%;
font-size: 18px;
}
.spanStyle {
margin-left: 2%;
font-size: 18px;
}
ol {
font-size: 18px;
}
#map {
height: 400px;
width: 100%;
}
#mapCluster {
height: 400px;
width: 100%;
}
#accessibleMarker {
height: 400px;
width: 100%;
}
.center {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
}
pre {
overflow-wrap: break-word;
}
</style>
<script>
// Initialize and add the map
function initMap() {
// -------------------------------- Variables ---------------------------------------------------
// The location of Washington DC
var dc = {lat: 38.893, lng: -77.036};
// The location of the White House
var whitehouse = {lat: 38.8977, lng: -77.0365};
// The location of Uluru
var uluru = {lat: -28.024, lng: 140.887};
var locations = [
{lat: -31.563910, lng: 147.154312},
{lat: -33.718234, lng: 150.363181},
{lat: -33.727111, lng: 150.371124},
{lat: -33.848588, lng: 151.209834},
{lat: -33.851702, lng: 151.216968},
{lat: -34.671264, lng: 150.863657},
{lat: -35.304724, lng: 148.662905},
{lat: -36.817685, lng: 175.699196},
{lat: -36.828611, lng: 175.790222},
{lat: -37.750000, lng: 145.116667},
{lat: -37.759859, lng: 145.128708},
{lat: -37.765015, lng: 145.133858},
{lat: -37.770104, lng: 145.143299},
{lat: -37.773700, lng: 145.145187},
{lat: -37.774785, lng: 145.137978},
{lat: -37.819616, lng: 144.968119},
{lat: -38.330766, lng: 144.695692},
{lat: -39.927193, lng: 175.053218},
{lat: -41.330162, lng: 174.865694},
{lat: -42.734358, lng: 147.439506},
{lat: -42.734358, lng: 147.501315},
{lat: -42.735258, lng: 147.438000},
{lat: -43.999792, lng: 170.463352}
]
// -------------------------------- The First Map ---------------------------------------------------
// The first map, centered at Washginton DC
var map = new google.maps.Map(
document.getElementById('map'), {zoom: 10, center: dc});
// The marker, positioned at Washington DC
var marker = new google.maps.Marker({position: dc, map: map}
);
// -------------------------------- The Second Map ---------------------------------------------------
// Create an array of alphabetical characters used to label the markers.
var labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
// The second map, centered at Uluru
var mapCluster = new google.maps.Map(
document.getElementById('mapCluster'), {zoom: 3, center: uluru});
// Add some markers to the map.
// Note: The code uses the JavaScript Array.prototype.map() method to
// create an array of markers based on a given "locations" array.
// The map() method here has nothing to do with the Google Maps API.
var markers = locations.map(function(location, i) {
return new google.maps.Marker({
position: location,
label: labels[i % labels.length],
map: mapCluster
});
});
//Add a marker clusterer to manage the markers.
var markerCluster = new MarkerClusterer(mapCluster, markers,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
// -------------------------------- The Third Map ---------------------------------------------------
// The third map, centered at the White House
var map = new google.maps.Map(
document.getElementById('accessibleMarker'), {zoom: 15, center: whitehouse});
var contentString = '<div id="content">'+
'<div id="siteNotice">'+
'</div>'+
'<h1 id="firstHeading" class="firstHeading">The White House</h1>'+
'<div id="bodyContent">'+
'<p>The <b>White House</b> is the official residence and workplace of ' +
'the president of the United States. It is located at 1600 Pennsylvania Avenue NW ' +
'in Washington, D.C. and has been the residence of every U.S. president since John Adams in 1800. ' +
'The term "White House" is often used as a metonym for the president and his advisers.' +
'<p>For more information, see <a href="https://en.wikipedia.org/wiki/White_House">'+
'The White House</a> on Wikipedia.'+
'</div>'+
'</div>';
// Adds the string from above to the Info Window
var infowindow = new google.maps.InfoWindow({
content: contentString
});
// The marker, positioned at Washington DC
var marker = new google.maps.Marker({position: whitehouse, map: map, title: 'The White House'});
//Adds an event listener for a click event to open the info window
marker.addListener('click', function() {
infowindow.open(map, marker);
});
var element = mapElements[i];
function makeKeyboardAccessible(element) {
element.setAttribute("tabindex","0");
element.setAttribute("role","button");
element.setAttribute("aria-label",element.title);
element.addEventListener("keydown", function(ev){
var key = ev.keyCode || ev.which;
if(key == 13 || key == 32){
var event = document.createEvent('HTMLEvents');
event.initEvent('click', true, false);
this.dispatchEvent(event);
} else if (key == 40) {//down
map.panBy(0, panY);
} else if (key == 38) {//up
map.panBy(0, -panY);
} else if (key == 37) {//left
map.panBy(-panX, 0);
} else if (key == 39) {//right
map.panBy(panX, 0);
} else {
return
}
ev.preventDefault();
});
}
// -------------------------------- The Close the Function ---------------------------------------------------
}
</script>
</head>
<body>
<div id="wrapper">
<div class="headingWrapper">
<h1>Accessible Map Example</h1>
</div>
<div class="headingWrapper">
<div id="toc" tabindex="-1">
<h2>Table of Contents</h2>
</div>
</div>
<ul>
<li>
<a href="#first">First Things First - Setting up the Map to Render in a Browser</a>
</li>
<li>
<a href="#second">Drawing the Map with a Marker</a>
</li>
<li>
<a href="#third">Marker Clustering</a>
</li>
<li>
<a href="#fourth">Extending the Basic Map Attributes</a>
</li>
<li>
<a href="#fifth">To Test</a>
</li>
</ul>
<p>In this example, I extend the accessibility of the Google Map API. This example is inspired by the <a href="https://equalentry.com/">Equal Entry</a> blog post <a href="https://equalentry.com/accessible-maps-on-the-web/">Accessible Maps on the Web</a>.</p>
<div class="headingWrapper">
<div id="first" tabindex="-1">
<h2>First Things First - Setting up the Map to Render in a Browser</h2>
</div>
</div>
<p>We have to crawl before we can walk and walk before we can run. The initial example I begin with is a basic map with a marker indicating a point on the map. To embed this on the page, I followed the <a href="https://developers.google.com/maps/documentation/javascript/adding-a-google-map">Adding a Google Map with a Marker to Your Website</a> tutorial on the Google Maps API. This example uses some basic markup to embed the map.</p>
<p>At a high level, the following steps must be taken to get a basic map with a marker working:</p>
<ol>
<li>Create a file with a small amount of HTML, CSS, and JavaScript. Save the file with a .html extension.</li>
<li>In the <a href="https://cloud.google.com/console/google/maps-apis/overview">Google Cloud Platform Console</a>, set up a project and enable the Maps JavaScript API in your project. Google typically gives about $300 in credits to start with and the cash value per page view count is nominal (fractions of a penny for requests in the thousands). More information can be found on the <a href="https://developers.google.com/maps/documentation/javascript/usage-and-billing">Maps JavaScript API Usage and Billing</a> page.</li>
<li>Once you have an account, you need to generate an API key. This allows your served requests to be tracked.</li>
<ul>
<li>Note: It is a Best Practice to <a href="https://developers.google.com/maps/api-key-best-practices#best_practice_list">Protect your API key</a>. This keeps your key from being used elsewhere maliciously.</li>
</ul>
</li>
</ol>
<p>Most of the important information is documented in the tutorial in the Maps JavaScript API however there are a few important notes. First, the billing is listed as optional. When I set this up, I was only able to serve the map twice. After that, the map shows an error saying the map didn't load correctly. You can troubleshoot this and learn more about the error in the Developer Console in your browser as well as the Google Cloud Platform Console, linked in the tutorial. In order to serve the map, you must now have billing set up with an account. Once this has been set up, requests to the API will be served and your usage will be tracked in the Google Cloud dashboard for the Map API.</p>
<p>Unfortunately, this means that you can no longer generate the map locally as it must be tracked through the Google Cloud Platform. The markup will load briefly when running the example locally then show an error that the map could not be displayed (shown below). For this example to work properly, I had to host the page and follow the aforementioned steps.</p>
<div class="center">
<img src="/images/map/google_maps_error.png" alt="Google Maps Request Error that reads 'Oops! Something went wrong. The page didn't load Google Maps correctly. See the JavaScript console for technical details.'" height="250" />
<br />
<span><i>IMAGE: Google Maps Request Error</i></span>
</div>
<div class="headingWrapper">
<div id="second" tabindex="-1">
<h2>Drawing the Map with a Marker</h2>
</div>
<a href="#toc">back to top</a>
</div>
<p>The first thing we do in our HTML file is create a <div> element with the ID "map". This will be where the map is drawn. We then apply some CSS styling to determine the size of the map.</p>
<p>In our JavaScript, we create variables and set the parameters for the map to be drawn. In the example below, I've created a variable called "dc" in which I've passed the latitude and longitude of Washington, DC. I then set the default zoom level and set the center of the map to be the variable for DC. Last, I add the marker at the DC position.</p>
<span class="spanStyle">HTML:</span>
<pre class="prettyprint lang-html"><div id="map"></div><br /><script async defer src="https://maps.googleapis.com/maps/api/js?key=[YOUR KEY]&callback=initMap"></script></pre>
<span class="spanStyle">CSS:</span>
<pre class="prettyprint lang-css">#map {<br /> height: 400px;<br /> width: 100%;<br />}</pre>
<span class="spanStyle">JavaScript:</span>
<pre class="prettyprint lang-js">// Initialize and add the map<br />function initMap() {<br /> // The location of Washington, DC<br /> var dc = {lat: 38.893, lng: -77.036};<br /> // The map, centered at Washington DC<br /> var map = new google.maps.Map(<br /> document.getElementById('map'), {zoom: 10, center: dc});<br /> // The marker, positioned at Washington DC<br /> var marker = new google.maps.Marker({position: dc, map: map});<br />}</pre>
<div id="map"></div>
<div class="headingWrapper">
<div id="third" tabindex="-1">
<h2>Marker Clustering</h2>
</div>
<a href="#toc">back to top</a>
</div>
<p>In this example, we place clusters to indicate groups of local markers. We place a <div> with an ID to render the map and style it just like in the previous example. I've also moved the script call,<head> of the document.</p>
<p>The full markup of the JavaScript for rendering both maps can be seen at <a href="https://jsfiddle.net/robertmc/hu29a4z8/">https://jsfiddle.net/robertmc/hu29a4z8/</a>.</p>
<span class="spanStyle">HTML:</span>
<pre class="prettyprint lang-html"><!-- In the <head> section --><br /><script async defer src="https://maps.googleapis.com/maps/api/js?key=[YOUR KEY]&callback=initMap"></script><br /><script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js"></script><br /><script src="https://unpkg.com/@google/markerclustererplus@4.0.1/dist/markerclustererplus.min.js"></script><br /><br /><!-- In the <body> section --><br /><div id="mapCluster"></div></pre>
<span class="spanStyle">CSS:</span>
<pre class="prettyprint lang-css">#mapCluster {<br /> height: 400px;<br /> width: 100%;<br />}</pre>
<span class="spanStyle">JavaScript:</span>
<pre class="prettyprint lang-js">// Initialize and add the map<br />function initMap() {<br /><br />// The location of Uluru<br />var uluru = {lat: -28.024, lng: 140.887};<br /><br />var locations = [<br /> {lat: -31.563910, lng: 147.154312},<br /> {lat: -33.718234, lng: 150.363181},<br /> {lat: -33.727111, lng: 150.371124},<br /> {lat: -33.848588, lng: 151.209834},<br /> {lat: -33.851702, lng: 151.216968},<br /> {lat: -34.671264, lng: 150.863657},<br /> {lat: -35.304724, lng: 148.662905},<br /> {lat: -36.817685, lng: 175.699196},<br /> {lat: -36.828611, lng: 175.790222},<br /> {lat: -37.750000, lng: 145.116667},<br /> {lat: -37.759859, lng: 145.128708},<br /> {lat: -37.765015, lng: 145.133858},<br /> {lat: -37.770104, lng: 145.143299},<br /> {lat: -37.773700, lng: 145.145187},<br /> {lat: -37.774785, lng: 145.137978},<br /> {lat: -37.819616, lng: 144.968119},<br /> {lat: -38.330766, lng: 144.695692},<br /> {lat: -39.927193, lng: 175.053218},<br /> {lat: -41.330162, lng: 174.865694},<br /> {lat: -42.734358, lng: 147.439506},<br /> {lat: -42.734358, lng: 147.501315},<br /> {lat: -42.735258, lng: 147.438000},<br /> {lat: -43.999792, lng: 170.463352}<br />]<br /><br />// Create an array of alphabetical characters used to label the markers<br />var labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';<br /><br />// The map, centered at Uluru<br />var mapCluster = new google.maps.Map(<br /> document.getElementById('mapCluster'), {zoom: 3, center: uluru});<br /><br /> // Add some markers to the map.<br /> // Note: The code uses the JavaScript Array.prototype.map() method to<br /> // create an array of markers based on a given "locations" array.<br /> // The map() method here has nothing to do with the Google Maps API.<br /> var markers = locations.map(function(location, i) {<br /> return new google.maps.Marker({<br /> position: location,<br /> label: labels[i % labels.length],<br /> map: mapCluster<br /> });<br /> });<br /><br /> //Add a marker clusterer to manage the markers.<br /> var markerCluster = new MarkerClusterer(mapCluster, markers,<br /> {imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});<br />}
</pre>
<div id="mapCluster"></div>
<div class="headingWrapper">
<div id="fourth" tabindex="-1">
<h2>Extending the Basic Map Attributes</h2>
</div>
<a href="#toc">back to top</a>
</div>
<p>We need to add various attributes to the map marker to ensure screen readers can identify the control's name and expected behavior</p>
<p>The <code>tabindex="0"</code> markup adds the marker to the keyboard focus order. The value of 0 means that the tabindex value is the order in which the content appears in the DOM. The <code>role="button"</code> markup ensures assistive technology can identify the control's role as that of a button. The <code>aria-label</code> markup applies an accessible name to the control.</p>
<span class="spanStyle">JavaScript:</span>
<pre class="prettyprint lang-js">function makeKeyboardAccessible(element) {<br /><br /> element.setAttribute("tabindex","0");<br /> element.setAttribute("role","button");<br /> element.setAttribute("aria-label",element.title);<br /><br /> element.addEventListener("keydown", function(ev){<br /> var key = ev.keyCode || ev.which;<br /> if(key == 13 || key == 32){<br /> var event = document.createEvent('HTMLEvents');<br /> event.initEvent('click', true, false);<br /> this.dispatchEvent(event);<br /> } else if (key == 40) {//down<br /> map.panBy(0, panY);<br /> } else if (key == 38) {//up<br /> map.panBy(0, -panY);<br /> } else if (key == 37) {//left<br /> map.panBy(-panX, 0);<br /> } else if (key == 39) {//right<br /> map.panBy(panX, 0);<br /> } else {<br /> return<br /> }<br /> ev.preventDefault();<br />});</pre>
<p>This listens for a click event to open the marker. The click event also provides keyboard support.</p>
<span class="spanStyle">JavaScript:</span>
<pre class="prettyprint lang-js">marker.addListener('click', function() {<br />infowindow.open(map, marker);<br />});</pre>
<div id="accessibleMarker"></div>
<div class="headingWrapper">
<div id="fifth" tabindex="-1">
<h2>To Test</h2>
</div>
<a href="#toc">back to top</a>
</div>
<p>To test this using the keyboard:</p>
<ul>
<li>Press <kbd>Tab</kbd> and <kbd>Shift</kbd> + <kbd>Tab</kbd> to navigate forwards and backwards between the actionable elements.</li>
<li>Navigate to the marker and press Enter to activate the dialog. Dialog best practices are followed: </li>
<ul>
<li>Focus is moved to the dialog as a whole when opened.</li>
<li>Keyboard focus is <strong>not</strong> contained within the dialog because it is a non-modal dialog opposed to a modal dialog.</li>
<li>The keyboard can be used to close the dialog. Either the "X" button can be activated or the <kbd>ESC</kbd> key can be used.</li>
<li>When closing the dialog, focus properly returns to the element that spawned it (the marker).</li>
</ul>
</ul>
<div class="center">
<img src="/images/inspector image.jpg" alt="Embedded Google map and Object Inspector" height="250" />
<br />
<span><i>IMAGE: Embedded Google map with marker highlighted in focus. The MSAA Object Inspector is visible showing the accessible properties.</i></span>
</div>
<footer>
<a href="http://robertjmccaffery.com/examples.html">Back to Examples</a> | <a href="http://robertjmccaffery.com/">Home</a>
</footer>
</div>
</body>
</html>