diff --git a/src/main/java/com/dedicatedcode/paikka/service/FileMetaDataProvider.java b/src/main/java/com/dedicatedcode/paikka/service/FileMetaDataProvider.java
new file mode 100644
index 0000000..ebf2d45
--- /dev/null
+++ b/src/main/java/com/dedicatedcode/paikka/service/FileMetaDataProvider.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of paikka.
+ *
+ * Paikka is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 or
+ * any later version.
+ *
+ * Paikka is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Affero General Public License for more details.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Paikka. If not, see .
+ */
+
+package com.dedicatedcode.paikka.service;
+
+import com.dedicatedcode.paikka.config.PaikkaConfiguration;
+import org.springframework.stereotype.Service;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+@Service
+public class FileMetaDataProvider implements MetaDataProvider {
+ private static final String METADATA_FILE_NAME = "paikka_metadata.json";
+
+ private final PaikkaConfiguration configuration;
+ private final Path metadataPath;
+
+ public FileMetaDataProvider(PaikkaConfiguration configuration) {
+ this.configuration = configuration;
+ this.metadataPath = Paths.get(configuration.getDataDir(), METADATA_FILE_NAME);
+ }
+
+ @Override
+ public boolean exists() {
+ return false;
+ }
+
+ @Override
+ public InputStream get() throws FileNotFoundException {
+ return new FileInputStream(metadataPath.toFile());
+ }
+}
diff --git a/src/main/java/com/dedicatedcode/paikka/service/MetaDataProvider.java b/src/main/java/com/dedicatedcode/paikka/service/MetaDataProvider.java
new file mode 100644
index 0000000..98f6eb6
--- /dev/null
+++ b/src/main/java/com/dedicatedcode/paikka/service/MetaDataProvider.java
@@ -0,0 +1,26 @@
+/*
+ * This file is part of paikka.
+ *
+ * Paikka is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 or
+ * any later version.
+ *
+ * Paikka is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Affero General Public License for more details.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Paikka. If not, see .
+ */
+
+package com.dedicatedcode.paikka.service;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public interface MetaDataProvider {
+ boolean exists();
+
+ InputStream get() throws FileNotFoundException;
+}
diff --git a/src/main/java/com/dedicatedcode/paikka/service/MetadataService.java b/src/main/java/com/dedicatedcode/paikka/service/MetadataService.java
index 309abdd..215153a 100644
--- a/src/main/java/com/dedicatedcode/paikka/service/MetadataService.java
+++ b/src/main/java/com/dedicatedcode/paikka/service/MetadataService.java
@@ -16,7 +16,6 @@
package com.dedicatedcode.paikka.service;
-import com.dedicatedcode.paikka.config.PaikkaConfiguration;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
@@ -25,11 +24,7 @@
import org.springframework.stereotype.Service;
import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.time.Instant;
-import java.time.format.DateTimeParseException;
+import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -40,17 +35,16 @@
public class MetadataService {
private static final Logger logger = LoggerFactory.getLogger(MetadataService.class);
- private static final String METADATA_FILE_NAME = "paikka_metadata.json";
- private final PaikkaConfiguration config;
private final ObjectMapper objectMapper;
+ private final MetaDataProvider provider;
private volatile PaikkaMetadata metadata; // Change type to PaikkaMetadata
- public MetadataService(PaikkaConfiguration config, ObjectMapper objectMapper) {
- this.config = config;
+ public MetadataService(MetaDataProvider provider, ObjectMapper objectMapper) {
+ this.provider = provider;
this.objectMapper = objectMapper;
- this.metadata = null; // Initialize with null, will be loaded in @PostConstruct
+ this.metadata = null;
}
@PostConstruct
@@ -62,20 +56,18 @@ public void init() {
* Loads the metadata from the paikka_metadata.json file.
* This method is synchronized to prevent race conditions during reload.
*/
- public synchronized void loadMetadata() {
- Path metadataPath = Paths.get(config.getDataDir(), METADATA_FILE_NAME);
-
- if (!Files.exists(metadataPath)) {
- logger.warn("Metadata file not found at {}. Running without metadata.", metadataPath);
+ private synchronized void loadMetadata() {
+ if (!provider.exists()) {
+ logger.warn("Metadata file not!. Running without metadata.");
this.metadata = null; // Set to null if file not found
return;
}
- try {
- this.metadata = objectMapper.readValue(metadataPath.toFile(), PaikkaMetadata.class); // Deserialize to PaikkaMetadata
- logger.info("Metadata loaded successfully from {}", metadataPath);
+ try (InputStream is = provider.get()) {
+ this.metadata = objectMapper.readValue(is, PaikkaMetadata.class); // Deserialize to PaikkaMetadata
+ logger.info("Metadata loaded successfully");
} catch (IOException e) {
- logger.error("Failed to load metadata from {}: {}", metadataPath, e.getMessage());
+ logger.error("Failed to load metadata:", e);
this.metadata = null; // Set to null on error
}
}
@@ -115,30 +107,4 @@ public Map getMetadata() {
metadataMap.put("paikkaVersion", metadata.paikkaVersion());
return Collections.unmodifiableMap(metadataMap);
}
-
- /**
- * Returns the import timestamp as an Instant.
- * If metadata is not available or timestamp is invalid, returns Optional.empty().
- */
- public Optional getImportTimestamp() {
- return Optional.ofNullable(metadata)
- .map(PaikkaMetadata::importTimestamp)
- .flatMap(timestampStr -> {
- try {
- return Optional.of(Instant.parse(timestampStr));
- } catch (DateTimeParseException e) {
- logger.warn("Invalid importTimestamp format in metadata: {}", timestampStr);
- return Optional.empty();
- }
- });
- }
-
- /**
- * Returns the PAIKKA application version that generated the data.
- */
- public String getPaikkaVersion() {
- return Optional.ofNullable(metadata)
- .map(PaikkaMetadata::paikkaVersion)
- .orElse("unknown");
- }
}
diff --git a/src/main/java/com/dedicatedcode/paikka/service/importer/ImportService.java b/src/main/java/com/dedicatedcode/paikka/service/importer/ImportService.java
index 56e5ffa..52e3190 100644
--- a/src/main/java/com/dedicatedcode/paikka/service/importer/ImportService.java
+++ b/src/main/java/com/dedicatedcode/paikka/service/importer/ImportService.java
@@ -36,6 +36,7 @@
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKBWriter;
import org.rocksdb.*;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
@@ -65,6 +66,7 @@ public class ImportService {
private final S2Helper s2Helper;
private final GeometrySimplificationService geometrySimplificationService;
private final PaikkaConfiguration config;
+ private final String version;
private final Map tagCache = new ConcurrentHashMap<>(1000);
@@ -73,10 +75,11 @@ public class ImportService {
private final AtomicLong sequence = new AtomicLong(0);
private final AtomicLong buildingSequence = new AtomicLong(0);
- public ImportService(S2Helper s2Helper, GeometrySimplificationService geometrySimplificationService, PaikkaConfiguration config) {
+ public ImportService(S2Helper s2Helper, GeometrySimplificationService geometrySimplificationService, PaikkaConfiguration config, @Value("${paikka.version:1.0.0}") String version) {
this.s2Helper = s2Helper;
this.geometrySimplificationService = geometrySimplificationService;
this.config = config;
+ this.version = version;
this.fileReadWindowSize = calculateFileReadWindowSize();
}
@@ -289,7 +292,7 @@ private void writeMetadataFile(List pbfFiles, Path dataDirectory) throws I
String importTimestamp = DateTimeFormatter.ISO_INSTANT.format(now);
String dataVersion = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss").withZone(ZoneOffset.UTC).format(now);
ObjectMapper objectMapper = new ObjectMapper();
- PaikkaMetadata metadata = new PaikkaMetadata(importTimestamp, dataVersion, pbfFiles.stream().map(path -> path.getFileName().toString()).toList(), S2Helper.GRID_LEVEL, "1.0.0");
+ PaikkaMetadata metadata = new PaikkaMetadata(importTimestamp, dataVersion, pbfFiles.stream().map(path -> path.getFileName().toString()).toList(), S2Helper.GRID_LEVEL, version);
objectMapper.writeValue(metadataPath.toFile(), metadata);
System.out.println("\n\033[1;32mMetadata file written to: " + metadataPath + "\033[0m");
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 9d40717..49537ed 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -12,6 +12,7 @@ spring.web.resources.cache.cachecontrol.cache-public=true
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/css/**,/js/**,/img/**,/fonts/**
+paikka.version=1.1.7
paikka.data-dir=./data
paikka.import.threads=16
paikka.import.chunk-size=100000
diff --git a/src/test/java/com/dedicatedcode/paikka/service/ImportServiceTest.java b/src/test/java/com/dedicatedcode/paikka/service/ImportServiceTest.java
index c3df746..0cd4751 100644
--- a/src/test/java/com/dedicatedcode/paikka/service/ImportServiceTest.java
+++ b/src/test/java/com/dedicatedcode/paikka/service/ImportServiceTest.java
@@ -68,7 +68,7 @@ void setUp() throws Exception {
GeometrySimplificationService geometrySimplificationService = new GeometrySimplificationService();
S2Helper s2Helper = new S2Helper();
- ImportService importService = new ImportService(s2Helper, geometrySimplificationService, config);
+ ImportService importService = new ImportService(s2Helper, geometrySimplificationService, config, "1.0.0");
importService.importData(Collections.singletonList(tempImportFile.toString()), tempDataDir.toString());
}
diff --git a/src/test/java/com/dedicatedcode/paikka/service/MetadataServiceTest.java b/src/test/java/com/dedicatedcode/paikka/service/MetadataServiceTest.java
new file mode 100644
index 0000000..af3dbbb
--- /dev/null
+++ b/src/test/java/com/dedicatedcode/paikka/service/MetadataServiceTest.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of paikka.
+ *
+ * Paikka is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 or
+ * any later version.
+ *
+ * Paikka is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Affero General Public License for more details.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Paikka. If not, see .
+ */
+
+package com.dedicatedcode.paikka.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class MetadataServiceTest {
+ @SuppressWarnings("unchecked")
+ @Test
+ void shouldLoadMetadata() {
+ MetadataService candidate = new MetadataService(new MetaDataProvider() {
+ @Override
+ public boolean exists() {
+ return true;
+ }
+
+ @Override
+ public InputStream get() throws FileNotFoundException {
+ return getClass().getResourceAsStream("/metadata.json");
+ }
+ }, new ObjectMapper());
+ candidate.reload();
+
+ Map loaded = candidate.getMetadata();
+ assertNotNull(loaded);
+ assertEquals("2026-05-17T07:47:51.028675223Z", loaded.get("importTimestamp"));
+ assertEquals("20260517-074751", loaded.get("dataVersion"));
+ assertEquals(12, loaded.get("gridLevel"));
+ assertEquals("1.0.0", loaded.get("paikkaVersion"));
+ List files = (List) loaded.get("files");
+ assertNotNull(files);
+ assertEquals(2, files.size());
+ assertEquals("planet-filtered.pbf", files.get(0));
+ assertEquals("test.pbf", files.get(1));
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/metadata.json b/src/test/resources/metadata.json
new file mode 100644
index 0000000..e960c62
--- /dev/null
+++ b/src/test/resources/metadata.json
@@ -0,0 +1 @@
+{"importTimestamp":"2026-05-17T07:47:51.028675223Z","dataVersion":"20260517-074751","file":["planet-filtered.pbf", "test.pbf"],"gridLevel":12,"paikkaVersion":"1.0.0"}
\ No newline at end of file