Java 下载 ERA5 数据:HttpClient + Gson,企业级开发者的选择
Python 统治了气象科研圈,但企业级系统大量运行在 Java 上。风电监控、光伏运维、农业平台、保险系统——很多需要气象数据的后端服务都是 Java 写的。今天用 Java 来获取 ERA5 数据。

核心思路
Java 调用 ERA5 数据最直接的方式就是 HTTP API:
Java 应用 → HTTP GET → 镜像地球 API → JSON 响应 → 解析为 Java 对象
不需要安装复杂的科学计算库,用标准 HTTP 客户端 + JSON 解析就够了。
准备工作
依赖(Maven)
<dependencies> <!-- Java 11+ 自带 HttpClient,无需额外依赖 --> <!-- JSON 解析 --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency> <!-- CSV 导出(可选) --> <dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> <version>5.8</version> </dependency></dependencies>
数据模型
public class HourlyData { private List<String> time; private List<Double> temperature_2m; private List<Double> precipitation; private List<Double> wind_speed_10m; // getters...}
方法一:基础调用(HttpClient)
最简单的单次查询,Java 11+ 原生 HttpClient:
import java.net.URI;import java.net.http.*;import java.net.http.HttpResponse.BodyHandlers;public class Era5Client { private static final String BASE_URL = "https://api.mirror-earth.com/v1/archive"; private final String apiKey; public Era5Client(String apiKey) { this.apiKey = apiKey; } public String fetchRaw(double lat, double lon, String start, String end, String hourlyVars) throws Exception { String url = BASE_URL + "?" + "latitude=" + lat + "&longitude=" + lon + "&hourly=" + hourlyVars + "&start_date=" + start + "&end_date=" + end + "&apikey=" + apiKey; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .GET() .build(); HttpResponse<String> response = client.send( request, BodyHandlers.ofString()); if (response.statusCode() == 200) { return response.body(); } else { throw new RuntimeException( "API 请求失败: " + response.statusCode() + " " + response.body()); } } public static void main(String[] args) throws Exception { Era5Client client = new Era5Client("your_api_key"); String json = client.fetchRaw( 39.9, 116.4, "2023-06-01", "2023-06-30", "temperature_2m,precipitation" ); System.out.println("获取到 " + json.length() + " 字节数据"); }}
方法二:带 JSON 解析(Gson)
import com.google.gson.Gson;import com.google.gson.JsonObject;import com.google.gson.JsonArray;import java.util.*;public class Era5Parser { public static List<Map<String, Object>> parseHourly(String json) { Gson gson = new Gson(); JsonObject root = gson.fromJson(json, JsonObject.class); JsonObject hourly = root.getAsJsonObject("hourly"); List<String> time = parseList(hourly, "time"); List<Double> temp = parseDoubleList(hourly, "temperature_2m"); List<Map<String, Object>> records = new ArrayList<>(); for (int i = 0; i < time.size(); i++) { Map<String, Object> record = new LinkedHashMap<>(); record.put("time", time.get(i)); record.put("temperature_2m", temp.get(i)); records.add(record); } return records; } public static void main(String[] args) throws Exception { Era5Client client = new Era5Client("your_key"); String json = client.fetchRaw( 39.9, 116.4, "2023-06-01", "2023-06-30", "temperature_2m,precipitation,wind_speed_10m" ); List<Map<String, Object>> data = parseHourly(json); System.out.println("共 " + data.size() + " 条逐小时数据"); // 找出最高温时刻 data.stream() .maxBy(Comparator.comparingDouble( m -> (Double) m.get("temperature_2m"))) .ifPresent(max -> System.out.println( "最高温: " + max.get("time") + " → " + max.get("temperature_2m") + "°C")); }}
方法三:批量查询多城市
import java.util.concurrent.*;public class BatchEra5Fetcher { private final Era5Client client; private final ExecutorService executor; public BatchEra5Fetcher(String apiKey, int threads) { this.client = new Era5Client(apiKey); this.executor = Executors.newFixedThreadPool(threads); } public Map<String, String> fetchCities( List<String[]> cities, String start, String end) throws Exception { Map<String, Future<String>> futures = new LinkedHashMap<>(); for (String[] city : cities) { String name = city[0]; double lat = Double.parseDouble(city[1]); double lon = Double.parseDouble(city[2]); futures.put(name, executor.submit(() -> client.fetchRaw(lat, lon, start, end, "temperature_2m,precipitation"))); } Map<String, String> results = new LinkedHashMap<>(); for (var entry : futures.entrySet()) { results.put(entry.getKey(), entry.getValue().get()); System.out.println("✓ " + entry.getKey() + " 完成"); } return results; } public void shutdown() { executor.shutdown(); } public static void main(String[] args) throws Exception { var fetcher = new BatchEra5Fetcher("your_key", 5); List<String[]> cities = List.of( new String[]{"北京", "39.9", "116.4"}, new String[]{"上海", "31.2", "121.5"}, new String[]{"广州", "23.1", "113.3"}, new String[]{"成都", "30.6", "104.1"}, new String[]{"武汉", "30.6", "114.3"} ); Map<String, String> results = fetcher.fetchCities( cities, "2023-06-01", "2023-06-30"); fetcher.shutdown(); System.out.println("全部完成,共 " + results.size() + " 个城市"); }}
方法四:导出 CSV
import com.opencsv.CSVWriter;import java.io.*;public class CsvExporter { public static void export(List<Map<String, Object>> data, String filePath) throws IOException { try (CSVWriter writer = new CSVWriter( new FileWriter(filePath))) { // 表头 String[] header = data.get(0).keySet() .toArray(new String[0]); writer.writeNext(header); // 数据行 for (Map<String, Object> record : data) { String[] row = record.values().stream() .map(Object::toString) .toArray(String[]::new); writer.writeNext(row); } } System.out.println("已导出 " + data.size() + " 条数据到 " + filePath); }}
方法五:Spring Boot 集成
企业项目中最常见的场景——把 ERA5 数据集成到 Spring Boot:
@Servicepublic class WeatherDataService { @Value("${mirror-earth.api-key}") private String apiKey; private final RestTemplate restTemplate; public WeatherDataService(RestTemplateBuilder builder) { this.restTemplate = builder.build(); } public Map<String, Object> getWeather(double lat, double lon, String start, String end) { String url = "https://api.mirror-earth.com/v1/archive?" + "latitude=" + lat + "&longitude=" + lon + "&hourly=temperature_2m,precipitation" + "&start_date=" + start + "&end_date=" + end + "&apikey=" + apiKey; return restTemplate.getForObject(url, Map.class); }}
@RestController@RequestMapping("/api/weather")public class WeatherController { @Autowired private WeatherDataService weatherService; @GetMapping("/history") public Map<String, Object> history( @RequestParam double lat, @RequestParam double lon, @RequestParam String start, @RequestParam String end) { return weatherService.getWeather(lat, lon, start, end); }}
对比:Java vs Python 获取 ERA5
|
|
|
|
|---|---|---|
| HTTP 调用 |
|
|
| JSON 解析 |
|
|
| 数据处理 |
|
|
| 科学计算 |
|
|
| 批量下载 |
|
|
| 部署 |
|
|
| 适合场景 |
|
|
Java 的优势在于集成到现有企业系统——如果你的系统本身就是 Spring Boot / Java 生态,直接用 HTTP API 获取 ERA5 数据是最自然的选择。
完整项目模板
建议的项目结构:
era5-java-client/├── pom.xml├── src/main/java/com/example/era5/│ ├── Era5Client.java # HTTP 客户端│ ├── Era5Parser.java # JSON 解析│ ├── CsvExporter.java # CSV 导出│ └── WeatherDataService.java # Spring Boot 服务├── src/main/resources/│ └── application.properties # API Key 配置└── src/test/java/ └── Era5ClientTest.java
夜雨聆风