Google Maps Integration
Google Maps Integration
UltraViolet Pro includes Google Maps integration for creating interactive maps with markers, custom styling, and advanced features.
Overview
Google Maps provides powerful mapping capabilities with:
- Interactive Maps - Pan, zoom, and explore locations
- Custom Markers - Add custom markers with info windows
- Custom Styling - Apply custom themes and colors
- Directions - Show routes and directions
- Places API - Search and display places
- Street View - Integrate Street View imagery
- Responsive Design - Works on all devices
Setup
API Key Configuration
First, obtain a Google Maps API key from the Google Cloud Console.
Add to your .env file:
GOOGLE_MAPS_API_KEY=your_api_key_here
Include Google Maps Script
<!-- Include Google Maps JavaScript API -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
Basic Map Implementation
HTML Structure
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Google Maps</h5>
</div>
<div class="card-body">
<div id="map" style="height: 500px; width: 100%;"></div>
</div>
</div>
JavaScript Initialization
function initMap() {
// Map options
const mapOptions = {
zoom: 10,
center: { lat: 40.7128, lng: -74.0060 }, // New York City
mapTypeId: google.maps.MapTypeId.ROADMAP,
styles: [
{
featureType: 'all',
elementType: 'geometry.fill',
stylers: [{ color: '#f5f5f5' }]
},
{
featureType: 'water',
elementType: 'geometry.fill',
stylers: [{ color: '#c9c9c9' }]
}
]
};
// Create map
const map = new google.maps.Map(document.getElementById('map'), mapOptions);
// Add marker
const marker = new google.maps.Marker({
position: { lat: 40.7128, lng: -74.0060 },
map: map,
title: 'New York City'
});
// Add info window
const infoWindow = new google.maps.InfoWindow({
content: '<h6>New York City</h6><p>The Big Apple</p>'
});
marker.addListener('click', function() {
infoWindow.open(map, marker);
});
}
Custom Markers
Basic Markers
// Create custom marker
const marker = new google.maps.Marker({
position: { lat: 40.7128, lng: -74.0060 },
map: map,
title: 'Custom Marker',
icon: {
url: '/images/marker-icon.png',
scaledSize: new google.maps.Size(32, 32)
}
});
Multiple Markers
const locations = [
{ lat: 40.7128, lng: -74.0060, title: 'New York' },
{ lat: 34.0522, lng: -118.2437, title: 'Los Angeles' },
{ lat: 41.8781, lng: -87.6298, title: 'Chicago' },
{ lat: 29.7604, lng: -95.3698, title: 'Houston' }
];
locations.forEach(location => {
const marker = new google.maps.Marker({
position: { lat: location.lat, lng: location.lng },
map: map,
title: location.title
});
const infoWindow = new google.maps.InfoWindow({
content: `<h6>${location.title}</h6>`
});
marker.addListener('click', function() {
infoWindow.open(map, marker);
});
});
Marker Clustering
// Include MarkerClusterer library
// <script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
const markers = locations.map(location => {
return new google.maps.Marker({
position: { lat: location.lat, lng: location.lng },
title: location.title
});
});
const markerCluster = new MarkerClusterer(map, markers, {
imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'
});
Custom Styling
Dark Theme
const darkTheme = [
{ elementType: 'geometry', stylers: [{ color: '#242f3e' }] },
{ elementType: 'labels.text.stroke', stylers: [{ color: '#242f3e' }] },
{ elementType: 'labels.text.fill', stylers: [{ color: '#746855' }] },
{
featureType: 'administrative.locality',
elementType: 'labels.text.fill',
stylers: [{ color: '#d59563' }]
},
{
featureType: 'poi',
elementType: 'labels.text.fill',
stylers: [{ color: '#d59563' }]
},
{
featureType: 'poi.park',
elementType: 'geometry',
stylers: [{ color: '#263c3f' }]
},
{
featureType: 'poi.park',
elementType: 'labels.text.fill',
stylers: [{ color: '#6b9a76' }]
},
{
featureType: 'road',
elementType: 'geometry',
stylers: [{ color: '#38414e' }]
},
{
featureType: 'road',
elementType: 'geometry.stroke',
stylers: [{ color: '#212a37' }]
},
{
featureType: 'road',
elementType: 'labels.text.fill',
stylers: [{ color: '#9ca5b3' }]
},
{
featureType: 'road.highway',
elementType: 'geometry',
stylers: [{ color: '#746855' }]
},
{
featureType: 'road.highway',
elementType: 'geometry.stroke',
stylers: [{ color: '#1f2835' }]
},
{
featureType: 'road.highway',
elementType: 'labels.text.fill',
stylers: [{ color: '#f3d19c' }]
},
{
featureType: 'transit',
elementType: 'geometry',
stylers: [{ color: '#2f3948' }]
},
{
featureType: 'transit.station',
elementType: 'labels.text.fill',
stylers: [{ color: '#d59563' }]
},
{
featureType: 'water',
elementType: 'geometry',
stylers: [{ color: '#17263c' }]
},
{
featureType: 'water',
elementType: 'labels.text.fill',
stylers: [{ color: '#515c6d' }]
},
{
featureType: 'water',
elementType: 'labels.text.stroke',
stylers: [{ color: '#17263c' }]
}
];
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: { lat: 40.7128, lng: -74.0060 },
styles: darkTheme
});
Custom Colors
const customStyle = [
{
featureType: 'all',
elementType: 'labels.text.fill',
stylers: [{ color: '#ffffff' }]
},
{
featureType: 'all',
elementType: 'labels.text.stroke',
stylers: [{ color: '#000000' }, { weight: 2 }]
},
{
featureType: 'water',
elementType: 'geometry.fill',
stylers: [{ color: '#0066cc' }]
},
{
featureType: 'landscape',
elementType: 'geometry.fill',
stylers: [{ color: '#00cc66' }]
}
];
Directions and Routes
Basic Directions
function initMap() {
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 7,
center: { lat: 41.85, lng: -87.65 }
});
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
const start = { lat: 40.7128, lng: -74.0060 }; // New York
const end = { lat: 34.0522, lng: -118.2437 }; // Los Angeles
directionsService.route({
origin: start,
destination: end,
travelMode: google.maps.TravelMode.DRIVING
}, (result, status) => {
if (status === 'OK') {
directionsRenderer.setDirections(result);
}
});
}
Directions with Waypoints
const waypoints = [
{ location: { lat: 39.9526, lng: -75.1652 }, stopover: true }, // Philadelphia
{ location: { lat: 38.9072, lng: -77.0369 }, stopover: true } // Washington DC
];
directionsService.route({
origin: start,
destination: end,
waypoints: waypoints,
travelMode: google.maps.TravelMode.DRIVING,
optimizeWaypoints: true
}, (result, status) => {
if (status === 'OK') {
directionsRenderer.setDirections(result);
}
});
Places Integration
Places Search
function initMap() {
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 40.7128, lng: -74.0060 },
zoom: 13
});
const service = new google.maps.places.PlacesService(map);
const request = {
query: 'restaurants in New York',
fields: ['name', 'geometry', 'formatted_address'],
locationBias: map.getCenter()
};
service.textSearch(request, (results, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK) {
results.forEach(place => {
const marker = new google.maps.Marker({
position: place.geometry.location,
map: map,
title: place.name
});
const infoWindow = new google.maps.InfoWindow({
content: `
<div>
<h6>${place.name}</h6>
<p>${place.formatted_address}</p>
</div>
`
});
marker.addListener('click', () => {
infoWindow.open(map, marker);
});
});
}
});
}
Autocomplete Search
<!-- Include Places library -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap"></script>
<input id="pac-input" class="form-control" type="text" placeholder="Search for places...">
<div id="map" style="height: 500px; width: 100%;"></div>
function initMap() {
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 40.7128, lng: -74.0060 },
zoom: 13
});
const input = document.getElementById('pac-input');
const searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
searchBox.addListener('places_changed', () => {
const places = searchBox.getPlaces();
if (places.length === 0) return;
const bounds = new google.maps.LatLngBounds();
places.forEach(place => {
if (!place.geometry || !place.geometry.location) return;
const marker = new google.maps.Marker({
map: map,
title: place.name,
position: place.geometry.location
});
bounds.extend(place.geometry.location);
});
map.fitBounds(bounds);
});
}
Street View Integration
Basic Street View
function initMap() {
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 40.7128, lng: -74.0060 },
zoom: 12
});
const streetViewService = new google.maps.StreetViewService();
const streetViewRenderer = new google.maps.StreetViewPanorama(document.getElementById('street-view'));
map.addListener('click', (event) => {
streetViewService.getPanorama({
location: event.latLng,
radius: 50
}, (data, status) => {
if (status === 'OK') {
streetViewRenderer.setPano(data.location.pano);
streetViewRenderer.setPov({
heading: 270,
pitch: 0
});
streetViewRenderer.setVisible(true);
}
});
});
}
Integration with Laravel
Controller Method
class MapController extends Controller
{
public function index()
{
$locations = Location::all();
return view('admin.maps.google', compact('locations'));
}
public function getLocations()
{
$locations = Location::select('id', 'name', 'latitude', 'longitude', 'description')
->get()
->map(function($location) {
return [
'id' => $location->id,
'name' => $location->name,
'lat' => $location->latitude,
'lng' => $location->longitude,
'description' => $location->description
];
});
return response()->json($locations);
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'latitude' => 'required|numeric',
'longitude' => 'required|numeric',
'description' => 'nullable|string'
]);
$location = Location::create($request->all());
return response()->json($location);
}
}
Frontend Integration
async function loadLocations() {
try {
const response = await fetch('/api/locations');
const locations = await response.json();
locations.forEach(location => {
const marker = new google.maps.Marker({
position: { lat: location.lat, lng: location.lng },
map: map,
title: location.name
});
const infoWindow = new google.maps.InfoWindow({
content: `
<div>
<h6>${location.name}</h6>
<p>${location.description || ''}</p>
<button onclick="editLocation(${location.id})" class="btn btn-sm btn-primary">Edit</button>
</div>
`
});
marker.addListener('click', () => {
infoWindow.open(map, marker);
});
});
} catch (error) {
console.error('Error loading locations:', error);
}
}
Responsive Design
CSS for Responsive Maps
.map-container {
position: relative;
width: 100%;
height: 500px;
overflow: hidden;
border-radius: 0.5rem;
}
@media (max-width: 768px) {
.map-container {
height: 300px;
}
}
@media (max-width: 576px) {
.map-container {
height: 250px;
}
}
Dynamic Resizing
function initMap() {
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 40.7128, lng: -74.0060 },
zoom: 10
});
// Resize map when window size changes
window.addEventListener('resize', () => {
google.maps.event.trigger(map, 'resize');
});
}
Best Practices
- API Key Security: Never expose API keys in client-side code
- Rate Limiting: Implement proper rate limiting for API calls
- Error Handling: Handle API errors gracefully
- Performance: Use marker clustering for large datasets
- Accessibility: Provide alternative text and keyboard navigation
- Mobile Optimization: Ensure maps work well on touch devices
- Caching: Cache map data when possible
Troubleshooting
Common Issues
- Map Not Loading: Check API key and billing status
- Markers Not Showing: Verify coordinates are valid
- Styling Issues: Ensure custom styles are properly formatted
- Performance: Use appropriate zoom levels and marker limits
Debug Mode
// Enable debug mode for development
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 40.7128, lng: -74.0060 },
zoom: 10,
gestureHandling: 'greedy',
disableDefaultUI: false
});
Examples in UltraViolet
Google Maps is used in several places throughout UltraViolet:
- Maps Overview: Basic map demonstrations
- Google Maps: Advanced examples with custom styling
- Dashboard: Interactive maps with real-time data
- Location Services: Place search and directions
For more examples, check the resources/views/admin/maps/ directory.