diff --git a/core/pom.xml b/core/pom.xml
index 39a82777679..07f570039e4 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -25,6 +25,7 @@
-->
yyyy-MM-dd'T'HH:mm:ss'Z'
${maven.build.timestamp}
+ 2.22.1
@@ -136,6 +137,45 @@
test
4.13.2
+
+ me.tongfei
+ progressbar
+ 0.10.0
+
+
+ org.apache.logging.log4j
+ log4j
+ pom
+ ${log4j.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-1.2-api
+ ${log4j.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.version}
+
+
+ org.apache.httpcomponents.core5
+ httpcore5
+ 5.3.1
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ 5.4.1
+
diff --git a/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java b/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java
index d6b599a2d8f..a39d89b6a44 100644
--- a/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java
+++ b/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java
@@ -21,6 +21,8 @@
import com.graphhopper.storage.DataAccess;
import com.graphhopper.util.BitUtil;
import com.graphhopper.util.Downloader;
+import com.graphhopper.util.ProgressBarLogger;
+import com.graphhopper.util.ProgressListener;
import java.io.File;
import java.io.FileNotFoundException;
@@ -49,7 +51,11 @@ public abstract class AbstractSRTMElevationProvider extends TileBasedElevationPr
public AbstractSRTMElevationProvider(String baseUrl, String cacheDir, String downloaderName, int minLat, int maxLat, int defaultWidth) {
super(cacheDir);
this.baseUrl = baseUrl;
- downloader = new Downloader(downloaderName).setTimeout(10000);
+
+ ProgressBarLogger.setLoggerName(AbstractTiffElevationProvider.class.getName());
+ ProgressListener progressListener = ProgressBarLogger.getProgressListener("Download elevation data");
+
+ this.downloader = new Downloader(downloaderName, progressListener).setTimeout(10000);
this.DEFAULT_WIDTH = defaultWidth;
this.MIN_LAT = minLat;
this.MAX_LAT = maxLat;
diff --git a/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java b/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java
index 0383d2af57b..79de216afd4 100644
--- a/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java
+++ b/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java
@@ -19,6 +19,8 @@
import com.graphhopper.storage.DataAccess;
import com.graphhopper.util.Downloader;
+import com.graphhopper.util.ProgressBarLogger;
+import com.graphhopper.util.ProgressListener;
import java.awt.image.Raster;
import java.io.File;
@@ -47,7 +49,11 @@ public abstract class AbstractTiffElevationProvider extends TileBasedElevationPr
public AbstractTiffElevationProvider(String baseUrl, String cacheDir, String downloaderName, int width, int height, int latDegree, int lonDegree) {
super(cacheDir);
this.baseUrl = baseUrl;
- this.downloader = new Downloader(downloaderName).setTimeout(10000);
+
+ ProgressBarLogger.setLoggerName(AbstractTiffElevationProvider.class.getName());
+ ProgressListener progressListener = ProgressBarLogger.getProgressListener("Download elevation data");
+
+ this.downloader = new Downloader(downloaderName, progressListener).setTimeout(10000);
this.WIDTH = width;
this.HEIGHT = height;
this.LAT_DEGREE = latDegree;
diff --git a/core/src/main/java/com/graphhopper/util/Downloader.java b/core/src/main/java/com/graphhopper/util/Downloader.java
index b01d2bf69e9..f53d851fd88 100644
--- a/core/src/main/java/com/graphhopper/util/Downloader.java
+++ b/core/src/main/java/com/graphhopper/util/Downloader.java
@@ -23,6 +23,13 @@
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
+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.HttpEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.Header;
/**
* @author Peter Karich
@@ -32,9 +39,15 @@ public class Downloader {
private String referrer = "http://graphhopper.com";
private String acceptEncoding = "gzip, deflate";
private int timeout = 4000;
+ private final ProgressListener progressListener;
- public Downloader(String userAgent) {
+ public Downloader(String userAgent, ProgressListener progressListener) {
this.userAgent = userAgent;
+ this.progressListener = progressListener;
+ }
+
+ public Downloader(String userAgent) {
+ this(userAgent, null);
}
public static void main(String[] args) throws IOException {
@@ -110,21 +123,60 @@ public HttpURLConnection createConnection(String urlStr) throws IOException {
}
public void downloadFile(String url, String toFile) throws IOException {
- HttpURLConnection conn = createConnection(url);
- InputStream iStream = fetch(conn, false);
- int size = 8 * 1024;
- BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(toFile), size);
- InputStream in = new BufferedInputStream(iStream, size);
- try {
- byte[] buffer = new byte[size];
- int numRead;
- while ((numRead = in.read(buffer)) != -1) {
- writer.write(buffer, 0, numRead);
+ final int bufferSize = 4096; // 4kB buffer
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ HttpGet httpGet = new HttpGet(url);
+
+ try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+ int statusCode = response.getCode();
+ if (statusCode < 200 || statusCode >= 300) {
+ throw new IOException("Server returned HTTP response code: " + statusCode + " for URL: " + url);
+ }
+
+ HttpEntity entity = response.getEntity();
+ if (entity == null) {
+ throw new IOException("No entity found in response for URL: " + url);
+ }
+
+ long contentLength = entity.getContentLength(); // Länge von der Entity holen
+ if (contentLength <= 0) {
+ Header lengthHeader = response.getFirstHeader("Content-Length");
+ if(lengthHeader != null) {
+ try {
+ contentLength = Long.parseLong(lengthHeader.getValue());
+ } catch (NumberFormatException e) {
+ contentLength = -1;
+ }
+ }
+ }
+
+ try (InputStream inputStream = entity.getContent(); // InputStream ist AutoCloseable
+ BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(toFile))) {
+
+ byte[] buffer = new byte[bufferSize];
+ int numRead;
+ long totalBytesRead = 0;
+ int lastPercentage = -1;
+
+ while ((numRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, numRead);
+ totalBytesRead += numRead;
+
+ if (progressListener != null && contentLength > 0) {
+ int currentPercentage = (int) (100 * totalBytesRead / (double) contentLength);
+ if (currentPercentage > lastPercentage) {
+ progressListener.update(currentPercentage);
+ lastPercentage = currentPercentage;
+ }
+ }
+ }
+ if (progressListener != null && contentLength > 0 && lastPercentage < 100) {
+ progressListener.update(100);
+ }
+ } finally {
+ EntityUtils.consume(entity);
+ }
}
- } finally {
- Helper.close(iStream);
- Helper.close(writer);
- Helper.close(in);
}
}
diff --git a/core/src/main/java/com/graphhopper/util/ProgressBarLogger.java b/core/src/main/java/com/graphhopper/util/ProgressBarLogger.java
new file mode 100644
index 00000000000..e4c53eecd07
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/util/ProgressBarLogger.java
@@ -0,0 +1,126 @@
+package com.graphhopper.util;
+
+import me.tongfei.progressbar.DelegatingProgressBarConsumer;
+import me.tongfei.progressbar.ProgressBar;
+import me.tongfei.progressbar.ProgressBarBuilder;
+import me.tongfei.progressbar.ProgressBarStyle;
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+
+
+public class ProgressBarLogger {
+ private static String LOGGER_NAME = "ProgressBarLogger";
+ private static final Logger CLASS_LOGGER = Logger.getLogger(ProgressBarLogger.class);
+
+
+ private static Logger initializeLogger() {
+ LoggerContext context = LoggerContext.getContext(false);
+ Configuration config = context.getConfiguration();
+ if (Logger.getRootLogger().getAppender(LOGGER_NAME) == null) {
+ String originalConsolePattern;
+ try {
+ originalConsolePattern = config.getAppender("Console").getLayout().getContentFormat().get("format");
+ } catch (Exception e) {
+ originalConsolePattern = "%d{yyyy-MM-dd HH:mm:ss} %highlight{%-7p} %style{%50t}{Cyan} %style{[ %-40.40c{1.} ]}{Bright Cyan} %m";
+ }
+
+ // Remove new line characters from the end of the pattern
+ if (originalConsolePattern.endsWith("%n")) {
+ originalConsolePattern = originalConsolePattern.substring(0, originalConsolePattern.length() - 2);
+ }
+
+ // Remove new line characters from the beginning of the pattern
+ if (originalConsolePattern.startsWith("%n")) {
+ originalConsolePattern = originalConsolePattern.substring(2);
+ }
+
+ final PatternLayout layout = PatternLayout.newBuilder()
+ .withPattern(originalConsolePattern + "\r")
+ .withConfiguration(config)
+ .build();
+
+ Appender consoleAppender = ConsoleAppender.newBuilder()
+ .setConfiguration(config)
+ .setName(LOGGER_NAME)
+ .setLayout(layout)
+ .setFilter(null)
+ .setTarget(ConsoleAppender.Target.SYSTEM_OUT)
+ .setName(LOGGER_NAME)
+ .setFollow(false)
+ .build();
+ consoleAppender.start();
+
+ // Create the new appender reference
+ AppenderRef ref = AppenderRef.createAppenderRef(LOGGER_NAME, null, null);
+ AppenderRef[] refs = new AppenderRef[]{ref};
+ LoggerConfig loggerConfig = LoggerConfig.newBuilder()
+ .withAdditivity(false)
+ .withLevel(Level.INFO)
+ .withLoggerName(LOGGER_NAME)
+ .withIncludeLocation("true")
+ .withRefs(refs)
+ .withProperties(null)
+ .withConfig(config)
+ .withtFilter(null)
+ .build();
+
+ config.addAppender(consoleAppender);
+ loggerConfig.addAppender(consoleAppender, null, null);
+ config.addLogger(LOGGER_NAME, loggerConfig);
+ context.updateLoggers();
+ }
+ return Logger.getLogger(LOGGER_NAME);
+ }
+
+ public static void setLoggerName(String loggerName) {
+ LOGGER_NAME = loggerName;
+ }
+
+ public static String getLoggerName() {
+ return LOGGER_NAME;
+ }
+
+ public static Logger getLogger() {
+ return initializeLogger();
+ }
+
+ /**
+ * Creates and returns a ProgressListener that receives percentages and updates a progress bar with the given task name.
+ *
+ * @param taskName the name of the task to be displayed in the progress bar
+ * @return a ProgressListener that updates the progress bar
+ */
+ public static ProgressListener getProgressListener(String taskName) {
+ Logger logger = initializeLogger();
+ return new ProgressListener() {
+ final ProgressBarBuilder progressBarBuilder = new ProgressBarBuilder()
+ .setStyle(ProgressBarStyle.COLORFUL_UNICODE_BAR)
+ .setUpdateIntervalMillis(1000) // slow update for better visualization and less IO. Avoids % calculation for each element.
+ .setInitialMax(100)
+ .setTaskName(taskName)
+ .setConsumer(new DelegatingProgressBarConsumer(logger::info));
+
+ ProgressBar progressBar;
+
+ int lastPercentage = 0;
+ @Override
+ public void update(long val) {
+ if (progressBar == null) {
+ progressBar = progressBarBuilder.build();
+ }
+
+ if (val > lastPercentage) {
+ progressBar.stepTo(val);
+ lastPercentage = (int) val;
+ }
+ }
+ };
+ }
+}