@@ -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>
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.rdkr.tide_display.backend.images.domain;
|
||||
|
||||
public enum ImageFormat {
|
||||
PNG,
|
||||
BINARY
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package com.rdkr.tide_display.backend.images.responses;
|
||||
|
||||
public record ImageResponse(int width, int height, byte[] data) {
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.rdkr.tide_display.backend.images.services;
|
||||
|
||||
public interface MapService {
|
||||
|
||||
byte[] getStaticMap(double latitude, double longitude, int zoom);
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user