google-site-verification: google61fe8ba583a51912.html
Integrating Google Maps Distance Matrix API in Java

The Google Maps Distance Matrix API is a powerful service that provides travel distance and time for a matrix of origins and destinations. It's incredibly useful for logistics, route planning, and applications that need to calculate multiple distances at once. In this article, we'll build a robust Java client to interact with this API.

Understanding the API

The Distance Matrix API:

  • Calculates travel distance and time between multiple origins and destinations
  • Supports various travel modes: driving, walking, bicycling, transit
  • Provides data for different transit modes and traffic models
  • Returns results in JSON or XML format

Project Setup

First, add the necessary dependencies to your pom.xml:

<dependencies> <!-- HTTP Client --> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- For logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.7</version> </dependency> </dependencies>

Java Implementation

1. Configuration Class

ApiConfig.java - Stores API configuration and constants

public class ApiConfig { private final String apiKey; private final String baseUrl; public ApiConfig(String apiKey) { this.apiKey = apiKey; this.baseUrl = "https://maps.googleapis.com/maps/api/distancematrix/json"; } public String getApiKey() { return apiKey; } public String getBaseUrl() { return baseUrl; } }

2. Request Models

TravelMode.java - Enum for different travel modes

public enum TravelMode { DRIVING("driving"), WALKING("walking"), BICYCLING("bicycling"), TRANSIT("transit"); private final String mode; TravelMode(String mode) { this.mode = mode; } public String getMode() { return mode; } }

TransitMode.java - Enum for transit options (optional)

public enum TransitMode { BUS("bus"), SUBWAY("subway"), TRAIN("train"), TRAM("tram"), RAIL("rail"); private final String mode; TransitMode(String mode) { this.mode = mode; } public String getMode() { return mode; } }

DistanceMatrixRequest.java - Builder pattern for constructing requests

import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class DistanceMatrixRequest { private final List<String> origins; private final List<String> destinations; private TravelMode mode; private String language; private String units; private String arrivalTime; private String departureTime; private List<TransitMode> transitModes; private String trafficModel; private boolean avoidTolls; private boolean avoidHighways; private boolean avoidFerries; private DistanceMatrixRequest(Builder builder) { this.origins = builder.origins; this.destinations = builder.destinations; this.mode = builder.mode; this.language = builder.language; this.units = builder.units; this.arrivalTime = builder.arrivalTime; this.departureTime = builder.departureTime; this.transitModes = builder.transitModes; this.trafficModel = builder.trafficModel; this.avoidTolls = builder.avoidTolls; this.avoidHighways = builder.avoidHighways; this.avoidFerries = builder.avoidFerries; } // Builder class public static class Builder { private final List<String> origins = new ArrayList<>(); private final List<String> destinations = new ArrayList<>(); private TravelMode mode = TravelMode.DRIVING; private String language = "en"; private String units = "metric"; private String arrivalTime; private String departureTime = "now"; private List<TransitMode> transitModes; private String trafficModel; private boolean avoidTolls = false; private boolean avoidHighways = false; private boolean avoidFerries = false; public Builder addOrigin(String origin) { this.origins.add(origin); return this; } public Builder addOrigins(List<String> origins) { this.origins.addAll(origins); return this; } public Builder addDestination(String destination) { this.destinations.add(destination); return this; } public Builder addDestinations(List<String> destinations) { this.destinations.addAll(destinations); return this; } public Builder withMode(TravelMode mode) { this.mode = mode; return this; } public Builder withLanguage(String language) { this.language = language; return this; } public Builder withUnits(String units) { this.units = units; return this; } public Builder withDepartureTime(String departureTime) { this.departureTime = departureTime; return this; } public Builder withTransitModes(List<TransitMode> transitModes) { this.transitModes = transitModes; return this; } public DistanceMatrixRequest build() { if (origins.isEmpty() || destinations.isEmpty()) { throw new IllegalStateException("Origins and destinations must not be empty"); } return new DistanceMatrixRequest(this); } } // Getters public List<String> getOrigins() { return origins; } public List<String> getDestinations() { return destinations; } public TravelMode getMode() { return mode; } public String getLanguage() { return language; } public String getUnits() { return units; } public String getArrivalTime() { return arrivalTime; } public String getDepartureTime() { return departureTime; } public List<TransitMode> getTransitModes() { return transitModes; } public String getTrafficModel() { return trafficModel; } public boolean isAvoidTolls() { return avoidTolls; } public boolean isAvoidHighways() { return avoidHighways; } public boolean isAvoidFerries() { return avoidFerries; } public String toQueryString() { StringBuilder sb = new StringBuilder(); // Add origins sb.append("origins=").append(String.join("|", origins)); // Add destinations sb.append("&destinations=").append(String.join("|", destinations)); // Add mode sb.append("&mode=").append(mode.getMode()); // Add language sb.append("&language=").append(language); // Add units sb.append("&units=").append(units); // Add departure time (required for transit mode) if (departureTime != null) { sb.append("&departure_time=").append(departureTime); } // Add transit modes if specified if (transitModes != null && !transitModes.isEmpty()) { String modes = transitModes.stream() .map(TransitMode::getMode) .collect(Collectors.joining("|")); sb.append("&transit_mode=").append(modes); } // Add avoidance parameters List<String> avoidances = new ArrayList<>(); if (avoidTolls) avoidances.add("tolls"); if (avoidHighways) avoidances.add("highways"); if (avoidFerries) avoidances.add("ferries"); if (!avoidances.isEmpty()) { sb.append("&avoid=").append(String.join("|", avoidances)); } return sb.toString(); } }

3. Response Models

DistanceMatrixResponse.java - Main response object

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) public class DistanceMatrixResponse { @JsonProperty("origin_addresses") private List<String> originAddresses; @JsonProperty("destination_addresses") private List<String> destinationAddresses; private List<Row> rows; private String status; @JsonProperty("error_message") private String errorMessage; // Getters and setters public List<String> getOriginAddresses() { return originAddresses; } public void setOriginAddresses(List<String> originAddresses) { this.originAddresses = originAddresses; } public List<String> getDestinationAddresses() { return destinationAddresses; } public void setDestinationAddresses(List<String> destinationAddresses) { this.destinationAddresses = destinationAddresses; } public List<Row> getRows() { return rows; } public void setRows(List<Row> rows) { this.rows = rows; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } }

Row.java - Represents a row in the distance matrix

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) public class Row { private List<Element> elements; public List<Element> getElements() { return elements; } public void setElements(List<Element> elements) { this.elements = elements; } }

Element.java - Individual distance/time element

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class Element { private Distance distance; private Duration duration; @JsonProperty("duration_in_traffic") private Duration durationInTraffic; private String status; // Getters and setters public Distance getDistance() { return distance; } public void setDistance(Distance distance) { this.distance = distance; } public Duration getDuration() { return duration; } public void setDuration(Duration duration) { this.duration = duration; } public Duration getDurationInTraffic() { return durationInTraffic; } public void setDurationInTraffic(Duration durationInTraffic) { this.durationInTraffic = durationInTraffic; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } }

Distance.java - Distance information

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class Distance { private String text; private int value; public String getText() { return text; } public void setText(String text) { this.text = text; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } }

Duration.java - Duration information

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class Duration { private String text; private int value; public String getText() { return text; } public void setText(String text) { this.text = text; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } }

4. Core Service Class

DistanceMatrixService.java - Main service class for API interactions

import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; public class DistanceMatrixService { private static final Logger logger = LoggerFactory.getLogger(DistanceMatrixService.class); private final ApiConfig config; private final CloseableHttpClient httpClient; private final ObjectMapper objectMapper; public DistanceMatrixService(ApiConfig config) { this.config = config; this.httpClient = HttpClients.createDefault(); this.objectMapper = new ObjectMapper(); } public DistanceMatrixResponse calculateDistanceMatrix(DistanceMatrixRequest request) { String url = buildRequestUrl(request); logger.info("Making request to: {}", url); HttpGet httpGet = new HttpGet(url); try (CloseableHttpResponse response = httpClient.execute(httpGet)) { String responseBody = EntityUtils.toString(response.getEntity()); if (response.getCode() != 200) { throw new RuntimeException("HTTP error: " + response.getCode() + " - " + responseBody); } DistanceMatrixResponse matrixResponse = objectMapper.readValue(responseBody, DistanceMatrixResponse.class); // Check API status if (!"OK".equals(matrixResponse.getStatus())) { throw new RuntimeException("API Error: " + matrixResponse.getStatus() + " - " + matrixResponse.getErrorMessage()); } return matrixResponse; } catch (IOException e) { throw new RuntimeException("Failed to execute Distance Matrix request", e); } } private String buildRequestUrl(DistanceMatrixRequest request) { return config.getBaseUrl() + "?" + request.toQueryString() + "&key=" + config.getApiKey(); } public void close() throws IOException { httpClient.close(); } }

5. Usage Example

DistanceMatrixDemo.java - Demonstration class

import java.util.Arrays; import java.util.List; public class DistanceMatrixDemo { public static void main(String[] args) { // Replace with your actual API key String apiKey = "YOUR_API_KEY_HERE"; ApiConfig config = new ApiConfig(apiKey); DistanceMatrixService service = new DistanceMatrixService(config); try { // Example 1: Simple driving distance between two points DistanceMatrixRequest request1 = new DistanceMatrixRequest.Builder() .addOrigin("New York, NY") .addOrigin("Boston, MA") .addDestination("Washington, DC") .addDestination("Philadelphia, PA") .withMode(TravelMode.DRIVING) .withUnits("imperial") .build(); System.out.println("=== Example 1: Multiple Origins to Multiple Destinations ==="); DistanceMatrixResponse response1 = service.calculateDistanceMatrix(request1); printResponse(response1); // Example 2: Transit with specific modes DistanceMatrixRequest request2 = new DistanceMatrixRequest.Builder() .addOrigin("Times Square, New York, NY") .addDestination("Central Park, New York, NY") .withMode(TravelMode.TRANSIT) .withTransitModes(Arrays.asList(TransitMode.SUBWAY, TransitMode.BUS)) .build(); System.out.println("\n=== Example 2: Transit Directions ==="); DistanceMatrixResponse response2 = service.calculateDistanceMatrix(request2); printResponse(response2); // Example 3: Walking distance DistanceMatrixRequest request3 = new DistanceMatrixRequest.Builder() .addOrigin("40.748817,-73.985428") // Coordinates (Empire State Building) .addDestination("40.758896,-73.987319") // Coordinates (Radio City Music Hall) .withMode(TravelMode.WALKING) .withUnits("metric") .build(); System.out.println("\n=== Example 3: Walking Directions with Coordinates ==="); DistanceMatrixResponse response3 = service.calculateDistanceMatrix(request3); printResponse(response3); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); e.printStackTrace(); } finally { try { service.close(); } catch (Exception e) { System.err.println("Error closing service: " + e.getMessage()); } } } private static void printResponse(DistanceMatrixResponse response) { List<String> origins = response.getOriginAddresses(); List<String> destinations = response.getDestinationAddresses(); for (int i = 0; i < origins.size(); i++) { System.out.println("\nFrom: " + origins.get(i)); Row row = response.getRows().get(i); for (int j = 0; j < destinations.size(); j++) { Element element = row.getElements().get(j); System.out.println(" To: " + destinations.get(j)); if ("OK".equals(element.getStatus())) { System.out.println(" Distance: " + element.getDistance().getText()); System.out.println(" Duration: " + element.getDuration().getText()); if (element.getDurationInTraffic() != null) { System.out.println(" Duration in Traffic: " + element.getDurationInTraffic().getText()); } } else { System.out.println(" Status: " + element.getStatus()); } } } } }

Expected Output

=== Example 1: Multiple Origins to Multiple Destinations === From: New York, NY, USA To: Washington, DC, USA Distance: 225 mi Duration: 3 hours 52 mins To: Philadelphia, PA, USA Distance: 94.6 mi Duration: 1 hour 44 mins From: Boston, MA, USA To: Washington, DC, USA Distance: 439 mi Duration: 7 hours 18 mins To: Philadelphia, PA, USA Distance: 305 mi Duration: 5 hours 15 mins

Best Practices and Considerations

  1. API Key Security: Never hardcode API keys. Use environment variables or secure configuration services.
  2. Rate Limiting: Implement retry logic with exponential backoff to handle rate limits.
  3. Caching: Cache frequent distance calculations to reduce API calls and costs.
  4. Error Handling: Always check the status field in the response for "OK", "ZERO_RESULTS", "NOT_FOUND", etc.
  5. Input Validation: Validate and sanitize addresses before making API calls.
  6. Cost Optimization: The Distance Matrix API has costs based on the number of elements (origins × destinations).

This implementation provides a robust, type-safe way to interact with the Google Maps Distance Matrix API, making it easy to integrate distance calculations into your Java applications.

Java Observability, Logging Intelligence & AI-Driven Monitoring (APM, Tracing, Logs & Anomaly Detection)

https://macronepal.com/blog/beyond-metrics-observing-serverless-and-traditional-java-applications-with-thundra-apm/
Explains using Thundra APM to observe both serverless and traditional Java applications by combining tracing, metrics, and logs into a unified observability platform for faster debugging and performance insights.

https://macronepal.com/blog/dynatrace-oneagent-in-java-2/
Explains Dynatrace OneAgent for Java, which automatically instruments JVM applications to capture metrics, traces, and logs, enabling full-stack monitoring and root-cause analysis with minimal configuration.

https://macronepal.com/blog/lightstep-java-sdk-distributed-tracing-and-observability-implementation/
Explains Lightstep Java SDK for distributed tracing, helping developers track requests across microservices and identify latency issues using OpenTelemetry-based observability.

https://macronepal.com/blog/honeycomb-io-beeline-for-java-complete-guide-2/
Explains Honeycomb Beeline for Java, which provides high-cardinality observability and deep query capabilities to understand complex system behavior and debug distributed systems efficiently.

https://macronepal.com/blog/lumigo-for-serverless-in-java-complete-distributed-tracing-guide-2/
Explains Lumigo for Java serverless applications, offering automatic distributed tracing, log correlation, and error tracking to simplify debugging in cloud-native environments. (Lumigo Docs)

https://macronepal.com/blog/from-noise-to-signals-implementing-log-anomaly-detection-in-java-applications/
Explains how to detect anomalies in Java logs using behavioral patterns and machine learning techniques to separate meaningful incidents from noisy log data and improve incident response.

https://macronepal.com/blog/ai-powered-log-analysis-in-java-from-reactive-debugging-to-proactive-insights/
Explains AI-driven log analysis for Java applications, shifting from manual debugging to predictive insights that identify issues early and improve system reliability using intelligent log processing.

https://macronepal.com/blog/titliel-java-logging-best-practices/
Explains best practices for Java logging, focusing on structured logs, proper log levels, performance optimization, and ensuring logs are useful for debugging and observability systems.

https://macronepal.com/blog/seeking-a-loguru-for-java-the-quest-for-elegant-and-simple-logging/
Explains the search for simpler, more elegant logging frameworks in Java, comparing modern logging approaches that aim to reduce complexity while improving readability and developer experience.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper