generate binary data of map picture
Signed-off-by: Peter Siegmund <peter@rdkr.com>
This commit is contained in:
@@ -37,6 +37,12 @@
|
||||
<artifactId>spring-boot-starter-cache</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>1.78.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
|
@@ -1,13 +1,17 @@
|
||||
package com.rdkr.tide_display.backend;
|
||||
|
||||
import java.security.Security;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
package com.rdkr.tide_display.backend.apple;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public interface Snapshot {
|
||||
|
||||
URI generateSnapshotURI(double latitude, double longitude);
|
||||
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.rdkr.tide_display.backend.apple.snapshot;
|
||||
|
||||
import com.rdkr.tide_display.backend.apple.Snapshot;
|
||||
import com.rdkr.tide_display.backend.crypto.Platforms;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Signature;
|
||||
import java.util.Base64;
|
||||
import lombok.val;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
@Component()
|
||||
public class SnapshotImpl implements Snapshot {
|
||||
|
||||
private final Platforms platforms;
|
||||
|
||||
@Value("${APPLE_TEAM_ID:dummy}")
|
||||
private String teamId;
|
||||
|
||||
@Value("${APPLE_KEY_ID:dummy}")
|
||||
private String keyId;
|
||||
|
||||
public SnapshotImpl(@Qualifier("AppleCrypto") Platforms platforms) {
|
||||
this.platforms = platforms;
|
||||
}
|
||||
|
||||
public URI generateSnapshotURI(double latitude, double longitude) {
|
||||
val uriBuilder = UriComponentsBuilder.fromUriString("https://snapshot.apple-mapkit.com/api/v1/snapshot")
|
||||
.queryParam("center", latitude + "," + longitude)
|
||||
.queryParam("t", "standard")
|
||||
.queryParam("scale", "1")
|
||||
.queryParam("size", "540x540")
|
||||
.queryParam("lang", "de-DE")
|
||||
.queryParam("poi", "0")
|
||||
.queryParam("teamId", teamId)
|
||||
.queryParam("keyId", keyId);
|
||||
return signRequest(uriBuilder);
|
||||
}
|
||||
|
||||
private URI signRequest(UriComponentsBuilder uriBuilder) {
|
||||
try {
|
||||
val pk = platforms.loadPrivateKey("MapKit_D28AJ2A3UT.p8");
|
||||
val signature = Signature.getInstance("SHA256withECDSA", "BC");
|
||||
signature.initSign(pk);
|
||||
signature.update(uriBuilder.toString().getBytes());
|
||||
val signatureBytes = new String(signature.sign(), StandardCharsets.UTF_8);
|
||||
val sign = Base64.getEncoder().encodeToString(signatureBytes.getBytes());
|
||||
return uriBuilder
|
||||
.queryParam("signature", sign)
|
||||
.build()
|
||||
.toUri();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.rdkr.tide_display.backend.common.config;
|
||||
|
||||
import com.rdkr.tide_display.backend.common.interceptor.RequestResponseLoggingInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
@Component
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
public RestClient.Builder restClient() {
|
||||
return RestClient
|
||||
.builder()
|
||||
.requestInterceptor(new RequestResponseLoggingInterceptor());
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.rdkr.tide_display.backend.common;
|
||||
package com.rdkr.tide_display.backend.common.config;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
@@ -12,6 +12,11 @@ public class CacheConfig {
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
return new ConcurrentMapCacheManager("map", "grayMap", "ePaper", "printHexValues");
|
||||
return new ConcurrentMapCacheManager(
|
||||
"privateKey",
|
||||
"map",
|
||||
"grayMap",
|
||||
"ePaper",
|
||||
"printHexValues");
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package com.rdkr.tide_display.backend.common.interceptor;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@Slf4j
|
||||
public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException
|
||||
{
|
||||
logRequest(request, body);
|
||||
ClientHttpResponse response = execution.execute(request, body);
|
||||
logResponse(response);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private void logRequest(HttpRequest request, byte[] body) throws IOException
|
||||
{
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug("=========================== request begin ================================================");
|
||||
log.debug("URI : {}", request.getURI());
|
||||
log.debug("Method : {}", request.getMethod());
|
||||
log.debug("Headers : {}", request.getHeaders());
|
||||
//log.debug("Request body: {}", new String(body, StandardCharsets.UTF_8));
|
||||
log.debug("=========================== request end ==================================================");
|
||||
}
|
||||
}
|
||||
|
||||
private void logResponse(ClientHttpResponse response) throws IOException
|
||||
{
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug("=========================== response begin ===============================================");
|
||||
log.debug("Status code : {}", response.getStatusCode());
|
||||
log.debug("Status text : {}", response.getStatusText());
|
||||
log.debug("Headers : {}", response.getHeaders());
|
||||
//log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8));
|
||||
log.debug("=========================== response end =================================================");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package com.rdkr.tide_display.backend.crypto;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
|
||||
public interface Platforms {
|
||||
|
||||
PrivateKey loadPrivateKey(String filename);
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.rdkr.tide_display.backend.crypto.platforms;
|
||||
|
||||
import com.rdkr.tide_display.backend.crypto.Platforms;
|
||||
import java.io.FileReader;
|
||||
import java.security.PrivateKey;
|
||||
import lombok.val;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
@Component("AppleCrypto")
|
||||
public class AppleCrypto implements Platforms {
|
||||
|
||||
@Cacheable(value = "privateKey", key = "#filename")
|
||||
public PrivateKey loadPrivateKey(String filename) {
|
||||
try {
|
||||
val file = ResourceUtils.getFile("classpath:apple/" + filename );
|
||||
val pemParser = new PEMParser(new FileReader(file));
|
||||
val converter = new JcaPEMKeyConverter();
|
||||
val object = (PrivateKeyInfo) pemParser.readObject();
|
||||
val pKey = converter.getPrivateKey(object);
|
||||
pemParser.close();
|
||||
return pKey;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load private key", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.rdkr.tide_display.backend.crypto.platforms;
|
||||
|
||||
import com.rdkr.tide_display.backend.crypto.Platforms;
|
||||
import java.security.PrivateKey;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("GoogleCrypto")
|
||||
public class GoogleCrypto implements Platforms {
|
||||
|
||||
@Cacheable(value = "privateKey", key = "#filename")
|
||||
public PrivateKey loadPrivateKey(String filename) {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -8,8 +8,8 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/")
|
||||
public class RootController {
|
||||
|
||||
@GetMapping()
|
||||
public String index() {
|
||||
return "Tide Display Backend";
|
||||
}
|
||||
@GetMapping()
|
||||
public String index() {
|
||||
return "Tide Display Backend";
|
||||
}
|
||||
}
|
||||
|
@@ -41,5 +41,5 @@ public class Firmware {
|
||||
private String espIdf;
|
||||
|
||||
private String compiled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,9 +5,12 @@ import java.util.List;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public interface StorageService {
|
||||
|
||||
List<Firmware> getFirmwareVersions();
|
||||
|
||||
byte[] download(String version, String filename);
|
||||
|
||||
String upload(MultipartFile file) throws IOException;
|
||||
|
||||
void save(String filename, byte[] data, String contentType, int expire);
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
|
||||
public class GoogleCloudBean {
|
||||
|
||||
@Bean
|
||||
public Storage getStorage(){
|
||||
public Storage getStorage() {
|
||||
return StorageOptions.getDefaultInstance().getService();
|
||||
}
|
||||
}
|
||||
|
@@ -8,9 +8,11 @@ import com.rdkr.tide_display.backend.gcp.StorageService;
|
||||
import jakarta.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -34,7 +36,8 @@ public class StorageServiceImpl implements StorageService {
|
||||
val result = new ArrayList<Firmware>();
|
||||
val bucket = storage.list(bucketName, BlobListOption.currentDirectory(), BlobListOption.prefix("firmware/"));
|
||||
for (val directory : bucket.iterateAll()) {
|
||||
val files = storage.list(bucketName, BlobListOption.currentDirectory(), BlobListOption.prefix(directory.getName()));
|
||||
val files = storage.list(bucketName, BlobListOption.currentDirectory(),
|
||||
BlobListOption.prefix(directory.getName()));
|
||||
for (val file : files.iterateAll()) {
|
||||
val name = file.getName();
|
||||
if (name.endsWith(".bin")) {
|
||||
@@ -53,7 +56,8 @@ public class StorageServiceImpl implements StorageService {
|
||||
val meta = new Firmware.FirmwareMeta(projectName, flashSize, espIdf, compiled);
|
||||
|
||||
result.add(
|
||||
new Firmware(major, minor, patch, "/files/" + major + "." + minor + "." + patch + "/firmware.bin", file.getEtag(), file.getUpdateTimeOffsetDateTime(), meta));
|
||||
new Firmware(major, minor, patch, "/files/" + major + "." + minor + "." + patch + "/firmware.bin",
|
||||
file.getEtag(), file.getUpdateTimeOffsetDateTime(), meta));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +88,19 @@ public class StorageServiceImpl implements StorageService {
|
||||
return null;
|
||||
}
|
||||
val version = extract(bytes, 48, 30);
|
||||
storage.create(BlobInfo.newBuilder(bucketName, "firmware/" + version + "/firmware.bin").build(), bytes);
|
||||
save("firmware/" + version + "/firmware.bin", bytes, MediaType.APPLICATION_OCTET_STREAM_VALUE, 0);
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(String filename, byte[] data, String contentType, int expire) {
|
||||
val metadata = new HashMap<String, String>();
|
||||
metadata.put("Cache-Control", "public, max-age=" + expire);
|
||||
val blobInfo = BlobInfo
|
||||
.newBuilder(bucketName, filename)
|
||||
.setContentType(contentType)
|
||||
.setMetadata(metadata)
|
||||
.build();
|
||||
storage.create(blobInfo, data);
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import com.rdkr.tide_display.backend.images.domain.ImageFormat;
|
||||
import com.rdkr.tide_display.backend.images.services.ImageConverterService;
|
||||
import com.rdkr.tide_display.backend.images.services.MapService;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
@@ -25,13 +26,14 @@ public class MapController {
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getMap(
|
||||
@RequestParam(required = false, defaultValue = "GoogleMaps") String mapProvider,
|
||||
@RequestParam(required = false, defaultValue = "53.541962") double latitude,
|
||||
@RequestParam(required = false, defaultValue = "9.993402") double longitude,
|
||||
@RequestParam(required = false, defaultValue = "15") int zoom,
|
||||
@RequestParam(required = false, defaultValue = "BINARY") ImageFormat format)
|
||||
throws IOException {
|
||||
val map = mapService.getStaticMap(latitude, longitude, zoom);
|
||||
var result = converterService.grayscale(map);
|
||||
val map = mapService.getStaticMap(mapProvider, latitude, longitude, zoom);
|
||||
var result = converterService.convert(map);
|
||||
val response = ResponseEntity.ok();
|
||||
if (ImageFormat.PNG.equals(format)) {
|
||||
return response.contentType(MediaType.IMAGE_PNG).body(result);
|
||||
@@ -40,4 +42,9 @@ public class MapController {
|
||||
return response.contentType(MediaType.APPLICATION_JSON).body(body);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/providers")
|
||||
public ResponseEntity<List<String>> getMapProviders() {
|
||||
return ResponseEntity.ok(mapService.getMapProviders());
|
||||
}
|
||||
}
|
||||
|
@@ -16,41 +16,35 @@ import org.springframework.stereotype.Service;
|
||||
@Slf4j
|
||||
public class ImageConverterService {
|
||||
|
||||
public byte[] grayscale(byte[] image) throws IOException {
|
||||
return grayscale_manual(image);
|
||||
}
|
||||
|
||||
public byte[] grayscale_automatic(byte[] image) throws IOException {
|
||||
public byte[] convert(byte[] image) throws IOException {
|
||||
val img = ImageIO.read(new ByteArrayInputStream(image));
|
||||
val grayImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
|
||||
val g = grayImage.getGraphics();
|
||||
g.drawImage(img, 0, 0, null);
|
||||
g.dispose();
|
||||
val out = new ByteArrayOutputStream();
|
||||
ImageIO.write(grayImage, "png", out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
val celShadedImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
public byte[] grayscale_manual(byte[] image) throws IOException {
|
||||
val img = ImageIO.read(new ByteArrayInputStream(image));
|
||||
val grayImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
|
||||
int rgb = 0, r = 0, g = 0, b = 0;
|
||||
for (int y = 0; y < img.getHeight(); y++) {
|
||||
for (int x = 0; x < img.getWidth(); x++) {
|
||||
rgb = img.getRGB(x, y);
|
||||
r = (rgb >> 16) & 0xFF;
|
||||
g = (rgb >> 8) & 0xFF;
|
||||
b = (rgb & 0xFF);
|
||||
rgb = (int) (r * 0.299 + g * 0.587 + b * 0.114);
|
||||
rgb = (255 << 24) | (rgb << 16) | (rgb << 8) | rgb;
|
||||
grayImage.setRGB(x, y, rgb);
|
||||
// Pixel-Farbe auslesen
|
||||
int rgb = img.getRGB(x, y);
|
||||
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
int gray = (r + g + b) / 3;
|
||||
|
||||
int celShadeLevel = gray / 16;
|
||||
int newGray = celShadeLevel * 16;
|
||||
|
||||
int newRGB = (newGray << 16) | (newGray << 8) | newGray;
|
||||
|
||||
celShadedImage.setRGB(x, y, newRGB);
|
||||
}
|
||||
}
|
||||
val out = new ByteArrayOutputStream();
|
||||
ImageIO.write(grayImage, "png", out);
|
||||
ImageIO.write(celShadedImage, "png", out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
public ImageResponse ePaperFormat(byte[] image) throws IOException {
|
||||
val img = ImageIO.read(new ByteArrayInputStream(image));
|
||||
val width = img.getWidth();
|
||||
@@ -74,11 +68,11 @@ public class ImageConverterService {
|
||||
result.write(b);
|
||||
}
|
||||
}
|
||||
return new ImageResponse(width, height, Base64.getEncoder().encode(result.toByteArray()));
|
||||
}
|
||||
|
||||
public void printHexValues(byte[] bytes) throws IOException {
|
||||
val hex = HexFormat.of().withUpperCase().withPrefix("0x").withSuffix(", ").formatHex(bytes);
|
||||
log.info("Hex value: {}", hex);
|
||||
val bytes = result.toByteArray();
|
||||
if (log.isDebugEnabled()) {
|
||||
val hex = HexFormat.of().withUpperCase().withPrefix("0x").withSuffix(", ").formatHex(bytes);
|
||||
log.debug("Image: {}", hex);
|
||||
}
|
||||
return new ImageResponse(width, height, Base64.getEncoder().encode(bytes));
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,11 @@
|
||||
package com.rdkr.tide_display.backend.images.services;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MapService {
|
||||
|
||||
byte[] getStaticMap(double latitude, double longitude, int zoom);
|
||||
List<String> getMapProviders();
|
||||
|
||||
byte[] getStaticMap(String provider, double latitude, double longitude, int zoom);
|
||||
|
||||
}
|
||||
|
@@ -1,25 +1,47 @@
|
||||
package com.rdkr.tide_display.backend.images.services;
|
||||
|
||||
import com.rdkr.tide_display.backend.gcp.StorageService;
|
||||
import com.rdkr.tide_display.backend.images.services.mapProvider.MapProvider;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
@Service
|
||||
public class MapServiceImpl implements MapService {
|
||||
|
||||
@Cacheable("map")
|
||||
public byte[] getStaticMap(double latitude, double longitude, int zoom) {
|
||||
val client = RestClient.create("https://maps.googleapis.com/maps/api/staticmap");
|
||||
return client.get()
|
||||
.uri(uriBuilder -> uriBuilder
|
||||
.queryParam("center", latitude + "," + longitude)
|
||||
.queryParam("zoom", "" + zoom)
|
||||
.queryParam("size", "540x540")
|
||||
.queryParam("map_id", "2f371c2346218fe8")
|
||||
.queryParam("key", "AIzaSyARgP_FKFXsrcgVd_HVWoIfH5N8-a88wlQ")
|
||||
.build())
|
||||
.retrieve()
|
||||
.body(byte[].class);
|
||||
private final Map<String, MapProvider> providers = new HashMap<>();
|
||||
private final StorageService storageService;
|
||||
|
||||
public MapServiceImpl(List<MapProvider> mapProviders, StorageService storageService) {
|
||||
this.storageService = storageService;
|
||||
mapProviders.forEach(
|
||||
provider -> providers.put(provider.getClass().getAnnotation(Component.class).value().toUpperCase(), provider));
|
||||
}
|
||||
|
||||
public List<String> getMapProviders() {
|
||||
return List.copyOf(providers.keySet());
|
||||
}
|
||||
|
||||
@Cacheable(value = "map", key = "{#provider, #latitude, #longitude, #zoom}")
|
||||
public byte[] getStaticMap(String provider, double latitude, double longitude, int zoom) {
|
||||
val mapProvider = providers.get(provider.toUpperCase());
|
||||
if (mapProvider == null) {
|
||||
throw new UnsupportedOperationException("Map provider not found: " + provider);
|
||||
}
|
||||
val imageData = mapProvider.getStaticMap(latitude, longitude, zoom);
|
||||
cacheImage(provider, latitude, longitude, zoom, imageData);
|
||||
return imageData;
|
||||
}
|
||||
|
||||
private void cacheImage(String provider, double latitude, double longitude, int zoom, byte[] image) {
|
||||
storageService.save("mapProvider/" + provider.toUpperCase() + "/" + latitude + "/" + longitude + "/" + zoom + "/image.png", image, MediaType.IMAGE_PNG_VALUE, 60);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package com.rdkr.tide_display.backend.images.services.mapProvider;
|
||||
|
||||
import com.rdkr.tide_display.backend.apple.Snapshot;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
@Component("AppleMapKit")
|
||||
@RequiredArgsConstructor
|
||||
public class AppleMapKit implements MapProvider {
|
||||
|
||||
private final RestClient.Builder restClient;
|
||||
private final Snapshot snapshot;
|
||||
|
||||
@Override
|
||||
public byte[] getStaticMap(double latitude, double longitude, int zoom) {
|
||||
try {
|
||||
val uri = snapshot.generateSnapshotURI(latitude, longitude);
|
||||
val client = restClient.baseUrl(uri.toString()).build();
|
||||
return client.get()
|
||||
.retrieve()
|
||||
.body(byte[].class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.rdkr.tide_display.backend.images.services.mapProvider;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
@Component("GoogleMaps")
|
||||
@RequiredArgsConstructor
|
||||
public class GoogleMaps implements MapProvider {
|
||||
|
||||
private final RestClient.Builder restClient;
|
||||
|
||||
@Value("${GCP_MAP_ID:dummy}")
|
||||
private String mapId;
|
||||
|
||||
@Value("${GCP_API_KEY:dummy}")
|
||||
private String apiKey;
|
||||
|
||||
@Override
|
||||
public byte[] getStaticMap(double latitude, double longitude, int zoom) {
|
||||
val client = restClient.baseUrl("https://maps.googleapis.com/maps/api/staticmap").build();
|
||||
return client.get()
|
||||
.uri(builder -> builder
|
||||
.queryParam("center", latitude + "," + longitude)
|
||||
.queryParam("zoom", "" + zoom)
|
||||
.queryParam("size", "540x540")
|
||||
.queryParam("map_id", mapId)
|
||||
.queryParam("key", apiKey)
|
||||
.build())
|
||||
.retrieve()
|
||||
.body(byte[].class);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.rdkr.tide_display.backend.images.services.mapProvider;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
@Component("MapBox")
|
||||
@RequiredArgsConstructor
|
||||
public class MapBox implements MapProvider {
|
||||
|
||||
private final RestClient.Builder restClient;
|
||||
|
||||
@Value("${MAPBOX_TOKEN:dummy}")
|
||||
private String accessToken;
|
||||
|
||||
@Override
|
||||
public byte[] getStaticMap(double latitude, double longitude, int zoom) {
|
||||
val client = restClient.baseUrl("https://api.mapbox.com/styles/v1/mapbox/streets-v12/static").build();
|
||||
return client.get()
|
||||
.uri(builder -> builder
|
||||
.path("/" + longitude + "," + latitude + "," + (zoom - 1))
|
||||
.path("/540x540")
|
||||
.queryParam("access_token", accessToken)
|
||||
.build())
|
||||
.retrieve()
|
||||
.body(byte[].class);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.rdkr.tide_display.backend.images.services.mapProvider;
|
||||
|
||||
public interface MapProvider {
|
||||
|
||||
byte[] getStaticMap(double latitude, double longitude, int zoom);
|
||||
|
||||
}
|
3
backend/src/main/resources/application-dev.yaml
Normal file
3
backend/src/main/resources/application-dev.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
logging:
|
||||
level:
|
||||
com.rdkr: trace
|
Reference in New Issue
Block a user