GeoGSON
This commit is contained in:
@ -10,8 +10,8 @@ spring:
|
||||
application:
|
||||
name: org-ccalm-main
|
||||
datasource:
|
||||
#url: jdbc:postgresql://10.0.0.1:5432/CCALM?ApplicationName=org_ccalm_main&connectTimeout=10000&socketTimeout=30000
|
||||
url: jdbc:postgresql://ccalm.org:5432/CCALM?ApplicationName=org_ccalm_main&ssl=true&sslmode=require&connectTimeout=10000&socketTimeout=10000
|
||||
url: jdbc:postgresql://10.0.0.1:5432/CCALM?ApplicationName=org_ccalm_main&connectTimeout=10000&socketTimeout=30000
|
||||
#url: jdbc:postgresql://ccalm.org:5432/CCALM?ApplicationName=org_ccalm_main&ssl=true&sslmode=require&connectTimeout=10000&socketTimeout=10000
|
||||
#url: jdbc:postgresql://127.0.0.1:5432/CCALM?ApplicationName=org_ccalm_main&ssl=true&sslmode=require&connectTimeout=10000&socketTimeout=10000
|
||||
username: postgres
|
||||
password: 309A86FF65A78FB428F4E38DFE35F730
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.ccalm.main;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
@ -8,6 +9,7 @@ import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@ -16,10 +18,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
//import javax.servlet.ServletContext;
|
||||
//import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
@ -253,31 +256,30 @@ public class GeoGSON implements ServletContextAware {
|
||||
|
||||
// JSON to file frmlocust_pods_density.qgs
|
||||
@RequestMapping(
|
||||
value = {"/gtest3","/geojson/frmlocust_pods_density", "/api/locust/v01/geojson/frmlocust_pods_density"},
|
||||
value = { "/geojson/frmlocust_pods_density", "/api/locust/v01/geojson/frmlocust_pods_density"},
|
||||
method = RequestMethod.GET,
|
||||
produces = "application/geo+json;charset=UTF-8"
|
||||
)
|
||||
@ResponseBody
|
||||
public Object podsDensity(
|
||||
public ResponseEntity<byte[]> podsDensity(
|
||||
@RequestParam(required = false, name = "country_id", defaultValue = "5") Integer countryId,
|
||||
@RequestParam(required = false, name = "date_from", defaultValue = "1750227418") Long dateFromUnix,
|
||||
@RequestParam(required = false, name = "date_to", defaultValue = "1758010618") Long dateToUnix,
|
||||
@CookieValue(value = "lng", defaultValue = "1") String language_id
|
||||
@CookieValue(value = "lng", defaultValue = "1") String language_id,
|
||||
HttpServletRequest request // Добавляем для получения заголовка Range
|
||||
) {
|
||||
try {
|
||||
String sql = """
|
||||
SELECT id,
|
||||
--country_name,
|
||||
--region_name,
|
||||
--date,
|
||||
COALESCE(pods,0) as pods,
|
||||
ST_AsGeoJSON(geom) AS geometry
|
||||
FROM main.view_frmlocust_pods_density
|
||||
SELECT
|
||||
fl.id,
|
||||
COALESCE(fl.eggs_capsules_density, fl.eggs_capsules_density_to)::double precision + COALESCE(fl.eggs_capsules_density_to, fl.eggs_capsules_density)::double precision / 2::double precision AS pods,
|
||||
ST_AsGeoJSON(st_setsrid(st_makepoint(fl.lon_center, fl.lat_center), 4326)) AS geometry
|
||||
FROM main.frmlocust fl
|
||||
WHERE 1=1
|
||||
AND geom IS NOT NULL
|
||||
AND (:countryId IS NULL OR country_id = :countryId)
|
||||
AND (:dateFrom IS NULL OR date >= to_timestamp(:dateFrom))
|
||||
AND (:dateTo IS NULL OR date <= to_timestamp(:dateTo))
|
||||
AND (:countryId IS NULL OR fl.country_id = :countryId)
|
||||
AND (:dateFrom IS NULL OR fl.date >= to_timestamp(:dateFrom))
|
||||
AND (:dateTo IS NULL OR fl.date <= to_timestamp(:dateTo))
|
||||
""";
|
||||
|
||||
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||
@ -292,19 +294,20 @@ public class GeoGSON implements ServletContextAware {
|
||||
for (Map<String, Object> row : rows) {
|
||||
JSONObject feature = new JSONObject();
|
||||
feature.put("type", "Feature");
|
||||
|
||||
// Геометрия
|
||||
feature.put("geometry", new JSONObject((String) row.get("geometry")));
|
||||
|
||||
// Атрибуты
|
||||
JSONObject props = new JSONObject();
|
||||
for (Map.Entry<String, Object> e : row.entrySet()) {
|
||||
if (!"geometry".equals(e.getKey())) {
|
||||
props.put(e.getKey(), e.getValue());
|
||||
Object value = e.getValue();
|
||||
if (value == null) {
|
||||
props.put(e.getKey(), JSONObject.NULL);
|
||||
} else {
|
||||
props.put(e.getKey(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
feature.put("properties", props);
|
||||
|
||||
features.put(feature);
|
||||
}
|
||||
|
||||
@ -312,85 +315,168 @@ public class GeoGSON implements ServletContextAware {
|
||||
collection.put("type", "FeatureCollection");
|
||||
collection.put("features", features);
|
||||
|
||||
return ResponseEntity.ok(collection.toString());
|
||||
// Преобразуем в байты
|
||||
byte[] data = collection.toString().getBytes(StandardCharsets.UTF_8);
|
||||
long contentLength = data.length;
|
||||
|
||||
// Обрабатываем Range-запрос
|
||||
String rangeHeader = request.getHeader("Range");
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Accept-Ranges", "bytes");
|
||||
|
||||
if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
|
||||
String[] ranges = rangeHeader.replace("bytes=", "").split("-");
|
||||
long start = Long.parseLong(ranges[0]);
|
||||
long end = ranges.length > 1 && !ranges[1].isEmpty() ? Long.parseLong(ranges[1]) : contentLength - 1;
|
||||
|
||||
if (start >= contentLength || end >= contentLength || start > end) {
|
||||
return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
|
||||
.header("Content-Range", "bytes */" + contentLength)
|
||||
.build();
|
||||
}
|
||||
|
||||
long rangeLength = end - start + 1;
|
||||
byte[] rangeData = new byte[(int) rangeLength];
|
||||
System.arraycopy(data, (int) start, rangeData, 0, (int) rangeLength);
|
||||
|
||||
headers.add("Content-Range", "bytes " + start + "-" + end + "/" + contentLength);
|
||||
headers.add("Content-Length", String.valueOf(rangeLength));
|
||||
|
||||
return new ResponseEntity<>(rangeData, headers, HttpStatus.PARTIAL_CONTENT);
|
||||
}
|
||||
|
||||
// Полный ответ, если Range не запрошен
|
||||
headers.add("Content-Length", String.valueOf(contentLength));
|
||||
return new ResponseEntity<>(data, headers, HttpStatus.OK);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("{\"error\":\"" + e.getMessage() + "\"}");
|
||||
String error = "{\"error\":\"" + e.getMessage() + "\"}";
|
||||
return new ResponseEntity<>(error.getBytes(StandardCharsets.UTF_8), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping(value = "/gtest", produces = "application/geo+json;charset=UTF-8")
|
||||
@GetMapping(
|
||||
value = { "/geojson/countriesdistricts", "/api/locust/v01/geojson/countriesdistricts"},
|
||||
produces = "application/geo+json;charset=UTF-8")
|
||||
@ResponseBody
|
||||
public String getTestGeoJson() throws Exception {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
// Формируем GeoJSON как объект
|
||||
String geoJson = """
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"geometry": {
|
||||
"coordinates": [70.15782, 42.2699],
|
||||
"type": "Point"
|
||||
},
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"date": "2025-09-13 17:55:00.0",
|
||||
"country_name": "Kazakhstan (Қазақстан)",
|
||||
"region_name": "Түркістан облысы (Туркестанская область)",
|
||||
"pods": 0,
|
||||
"id": 455603
|
||||
}
|
||||
},
|
||||
{
|
||||
"geometry": {
|
||||
"coordinates": [70.15782, 42.2699],
|
||||
"type": "Point"
|
||||
},
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"date": "2025-09-13 17:55:00.0",
|
||||
"country_name": "Kazakhstan (Қазақстан)",
|
||||
"region_name": "Түркістан облысы (Туркестанская область)",
|
||||
"pods": 0,
|
||||
"id": 455602
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
// Парсим и возвращаем для проверки корректности
|
||||
return mapper.readTree(geoJson).toString();
|
||||
public ResponseEntity<byte[]> getCountriesDistricts(
|
||||
@RequestParam(required = false, name = "country_id") Long countryId,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
return getGeoJson("main.view_countriesdistricts", "id", countryId, request);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/gtest2", produces = "application/geo+json;charset=UTF-8")
|
||||
@GetMapping(
|
||||
value = { "/geojson/countriesregions", "/api/locust/v01/geojson/countriesregions"},
|
||||
produces = "application/geo+json;charset=UTF-8"
|
||||
)
|
||||
@ResponseBody
|
||||
public String getTestGeoJson2() throws Exception {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
// Формируем GeoJSON как объект
|
||||
String geoJson = """
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [70.15782, 42.2699]
|
||||
},
|
||||
"properties": {
|
||||
"id": 1,
|
||||
"name": "Test point"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
// Парсим и возвращаем для проверки корректности
|
||||
return mapper.readTree(geoJson).toString();
|
||||
public ResponseEntity<byte[]> getCountriesRegions(
|
||||
@RequestParam(required = false, name = "country_id") Long countryId,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
return getGeoJson("main.view_countriesregions", "id", countryId, request);
|
||||
}
|
||||
|
||||
@GetMapping(
|
||||
value = { "/geojson/countries", "/api/locust/v01/geojson/countries"},
|
||||
produces = "application/geo+json;charset=UTF-8")
|
||||
@ResponseBody
|
||||
public ResponseEntity<byte[]> getCountries(
|
||||
@RequestParam(required = false, name = "country_id") Long countryId,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
return getGeoJson("main.view_countries", "country_id", countryId, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Общая функция для GeoJSON с поддержкой Range-запросов
|
||||
*/
|
||||
private ResponseEntity<byte[]> getGeoJson(
|
||||
String table,
|
||||
String idField,
|
||||
Long countryId,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
try {
|
||||
String sql = """
|
||||
SELECT
|
||||
%s AS id,
|
||||
ST_AsGeoJSON(geom)::text AS geometry,
|
||||
*
|
||||
FROM %s
|
||||
WHERE
|
||||
geom IS NOT NULL
|
||||
AND (country_id = :countryId)
|
||||
""".formatted(idField, table);
|
||||
|
||||
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||
params.addValue("countryId", countryId);
|
||||
|
||||
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, params);
|
||||
|
||||
JSONArray features = new JSONArray();
|
||||
for (Map<String, Object> row : rows) {
|
||||
JSONObject feature = new JSONObject();
|
||||
feature.put("type", "Feature");
|
||||
|
||||
// Геометрия
|
||||
feature.put("geometry", new JSONObject((String) row.get("geometry")));
|
||||
|
||||
// Свойства
|
||||
JSONObject props = new JSONObject();
|
||||
for (Map.Entry<String, Object> e : row.entrySet()) {
|
||||
if (!"geometry".equals(e.getKey()) && !"geom".equals(e.getKey())) {
|
||||
Object value = e.getValue();
|
||||
props.put(e.getKey(), value == null ? JSONObject.NULL : value);
|
||||
}
|
||||
}
|
||||
feature.put("properties", props);
|
||||
features.put(feature);
|
||||
}
|
||||
|
||||
JSONObject collection = new JSONObject();
|
||||
collection.put("type", "FeatureCollection");
|
||||
collection.put("features", features);
|
||||
|
||||
byte[] data = collection.toString().getBytes(StandardCharsets.UTF_8);
|
||||
long contentLength = data.length;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Accept-Ranges", "bytes");
|
||||
|
||||
// Обработка Range
|
||||
String rangeHeader = request.getHeader("Range");
|
||||
if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
|
||||
String[] ranges = rangeHeader.replace("bytes=", "").split("-");
|
||||
long start = Long.parseLong(ranges[0]);
|
||||
long end = ranges.length > 1 && !ranges[1].isEmpty() ? Long.parseLong(ranges[1]) : contentLength - 1;
|
||||
|
||||
if (start >= contentLength || end >= contentLength || start > end) {
|
||||
return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
|
||||
.header("Content-Range", "bytes */" + contentLength)
|
||||
.build();
|
||||
}
|
||||
|
||||
long rangeLength = end - start + 1;
|
||||
byte[] rangeData = new byte[(int) rangeLength];
|
||||
System.arraycopy(data, (int) start, rangeData, 0, (int) rangeLength);
|
||||
|
||||
headers.add("Content-Range", "bytes " + start + "-" + end + "/" + contentLength);
|
||||
headers.add("Content-Length", String.valueOf(rangeLength));
|
||||
|
||||
return new ResponseEntity<>(rangeData, headers, HttpStatus.PARTIAL_CONTENT);
|
||||
}
|
||||
|
||||
headers.add("Content-Length", String.valueOf(contentLength));
|
||||
return new ResponseEntity<>(data, headers, HttpStatus.OK);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
String error = "{\"error\":\"" + e.getMessage() + "\"}";
|
||||
return new ResponseEntity<>(error.getBytes(StandardCharsets.UTF_8), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user