add map image converter

Signed-off-by: Peter Siegmund <peter@rdkr.com>
This commit is contained in:
Peter Siegmund
2024-06-12 00:38:48 +02:00
parent 53d4bf2c5b
commit 171f49cb34
8 changed files with 191 additions and 0 deletions

View File

@@ -32,6 +32,10 @@
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -0,0 +1,17 @@
package com.rdkr.tide_display.backend.common;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("map", "grayMap", "ePaper", "printHexValues");
}
}

View File

@@ -0,0 +1,43 @@
package com.rdkr.tide_display.backend.images.controller;
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 lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/map")
@RequiredArgsConstructor
@Slf4j
public class MapController {
private final MapService mapService;
private final ImageConverterService converterService;
@GetMapping
public ResponseEntity<?> getMap(
@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 response = ResponseEntity.ok();
if (ImageFormat.PNG.equals(format)) {
return response.contentType(MediaType.IMAGE_PNG).body(result);
} else {
val body = converterService.ePaperFormat(result);
return response.contentType(MediaType.APPLICATION_JSON).body(body);
}
}
}

View File

@@ -0,0 +1,6 @@
package com.rdkr.tide_display.backend.images.domain;
public enum ImageFormat {
PNG,
BINARY
}

View File

@@ -0,0 +1,5 @@
package com.rdkr.tide_display.backend.images.responses;
public record ImageResponse(int width, int height, byte[] data) {
}

View File

@@ -0,0 +1,84 @@
package com.rdkr.tide_display.backend.images.services;
import com.rdkr.tide_display.backend.images.responses.ImageResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HexFormat;
import javax.imageio.ImageIO;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class ImageConverterService {
public byte[] grayscale(byte[] image) throws IOException {
return grayscale_manual(image);
}
public byte[] grayscale_automatic(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();
}
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);
}
}
val out = new ByteArrayOutputStream();
ImageIO.write(grayImage, "png", out);
return out.toByteArray();
}
public ImageResponse ePaperFormat(byte[] image) throws IOException {
val img = ImageIO.read(new ByteArrayInputStream(image));
val width = img.getWidth();
val height = img.getHeight();
val result = new ByteArrayOutputStream(image.length);
for (int y = 0; y < height; y++) {
int b = 0;
boolean done = true;
for (int x = 0; x < width; x++) {
val color = img.getRGB(x, y);
if (x % 2 == 0) {
b = color >> 4;
done = false;
} else {
b |= color & 0xF0;
result.write(b);
done = true;
}
}
if (!done) {
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);
}
}

View File

@@ -0,0 +1,7 @@
package com.rdkr.tide_display.backend.images.services;
public interface MapService {
byte[] getStaticMap(double latitude, double longitude, int zoom);
}

View File

@@ -0,0 +1,25 @@
package com.rdkr.tide_display.backend.images.services;
import lombok.val;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@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);
}
}