11extends ../layout
22
33block content
4- style .
5- #map {
6- width : 100% ;
7- height : 500px ;
8- }
9-
104 .pb-2.mt-2.mb-4.border-bottom
115 h2
12- i.fab .fa-google .fa-sm.iconpadding
13- | Google Maps API
6+ i.fas .fa-map .fa-sm.iconpadding
7+ | Google Maps JavaScript API
148
159 .btn-group.d-flex ( role ='group' )
1610 a.btn.btn-primary.w-100 ( href ='https://developers.google.com/maps/documentation/javascript/tutorial' , target ='_blank' )
@@ -20,139 +14,195 @@ block content
2014 i.fas.fa-laptop.fa-sm.iconpadding
2115 | API Console
2216
23- h3 Markers Example
17+ br
18+ p This example uses custom markers with Font Awesome icons, a custom map control (Center Map), and restricted navigation boundaries.
2419
25- #map
20+ .row
21+ .col-md-12
22+ #map ( style ='height: 500px' )
2623
24+ script( src ='https://polyfill.io/v3/polyfill.min.js?features=default' )
25+ script( src =` https://maps.googleapis.com/maps/api/js?key=${ google_map_api_key} &libraries=marker&loading=async` )
2726 script .
28- var map;
29- var superchargers = [
30- {
31- location: ' Fremont' ,
32- latitude: 37.49267 ,
33- longitude: - 121.94409 ,
34- },
35- {
36- location: ' Folsom' ,
37- latitude: 38.64392 ,
38- longitude: - 121.18621 ,
39- },
40- {
41- location: ' Gilroy' ,
42- latitude: 37.02615 ,
43- longitude: - 121.56487 ,
44- },
45- {
46- location: ' Harris Ranch' ,
47- latitude: 36.25316 ,
48- longitude: - 120.23853 ,
49- },
50- {
51- location: ' Mt. Shasta' ,
52- latitude: 41.30981 ,
53- longitude: - 122.31766 ,
54- },
55- {
56- location: ' Roseville' ,
57- latitude: 38.77344 ,
58- longitude: - 121.26928 ,
59- },
60- {
61- location: ' Truckee' ,
62- latitude: 39.3272 ,
63- longitude: - 120.20643 ,
64- },
65- {
66- location: ' Vacaville' ,
67- latitude: 38.3672 ,
68- longitude: - 121.95601 ,
69- },
70- {
71- location: ' Atascadero' ,
72- latitude: 35.4864 ,
73- longitude: - 120.66558 ,
74- },
75- {
76- location: ' Barstow' ,
77- latitude: 34.8912 ,
78- longitude: - 116.9991 ,
79- },
80- {
81- location: ' Buellton' ,
82- latitude: 34.61545 ,
83- longitude: - 120.18856 ,
84- },
85- {
86- location: ' Cabazon' ,
87- latitude: 33.92967 ,
88- longitude: - 116.81649 ,
89- },
90- {
91- location: ' Culver City' ,
92- latitude: 33.98465 ,
93- longitude: - 118.39505 ,
94- },
95- {
96- location: ' El Centro' ,
97- latitude: 32.762 ,
98- longitude: - 115.53197 ,
99- },
100- {
101- location: ' Indio' ,
102- latitude: 33.74272 ,
103- longitude: - 116.21316 ,
104- },
105- {
106- location: ' Los Angeles' ,
107- latitude: 33.92142 ,
108- longitude: - 118.32982 ,
109- },
110- {
111- location: ' Needles' ,
112- latitude: 34.85083 ,
113- longitude: - 114.62414 ,
114- },
115-
116- {
117- location: ' Oxnard' ,
118- latitude: 34.24075 ,
119- longitude: - 119.17699 ,
120- },
121- {
122- location: ' Rancho Cucamonga' ,
123- latitude: 34.11348 ,
124- longitude: - 117.53257 ,
125- },
126- {
127- location: ' San Juan Capistrano' ,
128- latitude: 33.49835 ,
129- longitude: - 117.66287 ,
130- },
131- {
132- location: ' Tejon Ranch' ,
133- latitude: 34.98661 ,
134- longitude: - 118.9463 ,
135- },
136- ];
137-
138- function initMap () {
139- map = new google.maps.Map (document .getElementById (' map' ), {
140- center: { lat: 36.0907578 , lng: - 119.5948303 },
141- zoom: 7 ,
142- });
27+ let map;
28+ let mapLoaded = false ;
29+
30+ class CustomMarker {
31+ constructor (position , icon , title ) {
32+ const markerElement = document .createElement (' div' );
33+ markerElement .className = ' custom-marker' ;
34+
35+ const container = document .createElement (' div' );
36+ container .style .position = ' relative' ;
37+ container .style .cursor = ' pointer' ;
38+ container .style .textAlign = ' center' ;
39+
40+ // Create pin shape with pseudo-element
41+ const pin = document .createElement (' div' );
42+ pin .style .background = ' rgb(231, 167, 149)' ;
43+ pin .style .width = ' 30px' ;
44+ pin .style .height = ' 40px' ;
45+ pin .style .borderRadius = ' 50% 50% 50% 0' ;
46+ pin .style .transform = ' rotate(-45deg)' ;
47+ pin .style .boxShadow = ' 0 2px 6px rgba(0, 0, 0, 0.73)' ;
48+ pin .style .margin = ' 0 auto' ;
49+
50+ // Container for the icon
51+ const iconContainer = document .createElement (' div' );
52+ iconContainer .style .position = ' absolute' ;
53+ iconContainer .style .top = ' 12px' ;
54+ iconContainer .style .left = ' 0' ;
55+ iconContainer .style .right = ' 0' ;
56+
57+ const iconElement = document .createElement (' i' );
58+ iconElement .className = ` fas ${ icon} ` ;
59+ iconElement .style .color = ' rgb(104, 32, 32)' ;
60+ iconElement .style .fontSize = ' 14px' ;
61+
62+ // Add text label below the pin
63+ const label = document .createElement (' div' );
64+ label .style .marginTop = ' 0px' ; // Space between pin and text
65+ label .style .color = ' rgb(78, 25, 25)' ;
66+ label .style .fontSize = ' 14px' ;
67+ label .style .fontWeight = ' bold' ;
68+ label .style .whiteSpace = ' nowrap' ;
69+ label .style .textShadow =
70+ ' -1px -1px 0 #fff,' + // Top-left
71+ ' 1px -1px 0 #fff,' + // Top-right
72+ ' -1px 1px 0 #fff,' + // Bottom-left
73+ ' 1px 1px 0 #fff' ; // Bottom-right
74+ label .textContent = title;
14375
144- superchargers .forEach (function (sc ) {
145- var marker = new google.maps.Marker ({
146- position: new google.maps.LatLng (sc .latitude , sc .longitude ),
147- icon: {
148- url: 'data:image/png;base64,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',
149- scaledSize: new google.maps.Size (23 , 33 ),
76+ iconContainer .appendChild (iconElement);
77+ container .appendChild (pin);
78+ container .appendChild (iconContainer);
79+ container .appendChild (label);
80+ markerElement .appendChild (container);
81+
82+ return new google.maps.marker.AdvancedMarkerElement ({
83+ position,
84+ content: markerElement,
85+ title,
86+ });
87+ }
88+ }
89+
90+ async function initMap () {
91+ if (mapLoaded) return ;
92+
93+ try {
94+ map = new google.maps.Map (document .getElementById (' map' ), {
95+ center: { lat: 37.7749 , lng: - 122.4194 },
96+ zoom: 13 ,
97+ maxZoom: 15 ,
98+ minZoom: 10 ,
99+ mapId: ' MapID001' ,
100+ mapTypeId: ' roadmap' ,
101+ gestureHandling: ' cooperative' ,
102+ restriction: {
103+ latLngBounds: {
104+ north: 37.85 ,
105+ south: 37.7 ,
106+ east: - 122.35 ,
107+ west: - 122.52 ,
108+ },
109+ strictBounds: true ,
110+ },
111+ mapTypeControl: true ,
112+ mapTypeControlOptions: {
113+ style: google .maps .MapTypeControlStyle .DROPDOWN_MENU ,
114+ position: google .maps .ControlPosition .TOP_RIGHT ,
115+ },
116+ zoomControl: true ,
117+ zoomControlOptions: {
118+ position: google .maps .ControlPosition .RIGHT_CENTER ,
119+ },
120+ scaleControl: true ,
121+ streetViewControl: true ,
122+ streetViewControlOptions: {
123+ position: google .maps .ControlPosition .RIGHT_TOP ,
150124 },
151- map: map,
152- title: sc .location ,
153- animation: google .maps .Animation .DROP ,
125+ fullscreenControl: true ,
154126 });
155- });
127+
128+ const locations = [
129+ {
130+ position: { lat: 37.7749 , lng: - 122.4194 },
131+ title: ' San Francisco' ,
132+ content: ' The cultural, commercial, and financial center of Northern California' ,
133+ icon: ' fa-city' ,
134+ },
135+ {
136+ position: { lat: 37.7858 , lng: - 122.4064 },
137+ title: ' Financial District' ,
138+ content: " San Francisco's business and financial hub" ,
139+ icon: ' fa-landmark' ,
140+ },
141+ {
142+ position: { lat: 37.8019 , lng: - 122.4189 },
143+ title: " Fisherman's Wharf" ,
144+ content: ' Famous waterfront neighborhood with seafood restaurants' ,
145+ icon: ' fa-fish' ,
146+ },
147+ ];
148+
149+ const infoWindow = new google.maps.InfoWindow ();
150+
151+ // Wait for the marker library to load
152+ await google .maps .importLibrary (' marker' );
153+
154+ locations .forEach ((location ) => {
155+ const marker = new CustomMarker (location .position , location .icon , location .title );
156+
157+ marker .map = map;
158+
159+ marker .addEventListener (' gmp-click' , () => {
160+ infoWindow .setContent (`
161+ <div style="padding: 10px;">
162+ <h3><i class="fas ${ location .icon } "></i> ${ location .title } </h3>
163+ <p>${ location .content } </p>
164+ </div>
165+ ` );
166+ infoWindow .open ({
167+ anchor: marker,
168+ map,
169+ });
170+ });
171+ });
172+
173+ const centerControl = document .createElement (' div' );
174+ centerControl .style .backgroundColor = ' #fff' ;
175+ centerControl .style .border = ' 2px solid #fff' ;
176+ centerControl .style .borderRadius = ' 3px' ;
177+ centerControl .style .boxShadow = ' 0 2px 6px rgba(0,0,0,.3)' ;
178+ centerControl .style .cursor = ' pointer' ;
179+ centerControl .style .marginTop = ' 10px' ;
180+ centerControl .style .marginRight = ' 10px' ;
181+ centerControl .style .padding = ' 8px' ;
182+ centerControl .style .textAlign = ' center' ;
183+ centerControl .innerHTML = ' Center Map' ;
184+ centerControl .addEventListener (' click' , () => {
185+ map .setCenter ({ lat: 37.7749 , lng: - 122.4194 });
186+ map .setZoom (13 );
187+ });
188+
189+ map .controls [google .maps .ControlPosition .TOP_RIGHT ].push (centerControl);
190+ mapLoaded = true ;
191+ } catch (error) {
192+ console .error (' Error initializing map:' , error);
193+ }
156194 }
157195
158- script( async , defer , src =` https://maps.googleapis.com/maps/api/js?key=${ google_map_api_key} &callback=initMap` )
196+ // Intersection Observer to load map only when visible
197+ const observer = new IntersectionObserver ((entries ) => {
198+ entries .forEach ((entry ) => {
199+ if (entry .isIntersecting ) {
200+ initMap ();
201+ observer .disconnect ();
202+ }
203+ });
204+ });
205+
206+ window .addEventListener (' load' , () => {
207+ observer .observe (document .getElementById (' map' ));
208+ });
0 commit comments