※当サイトの記事には、広告・プロモーションが含まれます。

GeoJSONをデシリアライズしてSpring Data JPAでPostgreSQLにINSERTしてみる

www.itmedia.co.jp

⇧ 規模がえぐいて...。

GeoJSONとは?

Wikipediaさんによりますと、

GeoJSON is an open standard format designed for representing simple geographical features, along with their non-spatial attributes. It is based on the JSON format.

https://en.wikipedia.org/wiki/GeoJSON

The GeoJSON format differs from other GIS standards in that it was written and is maintained not by a formal standards organization, but by an Internet working group of developers.

https://en.wikipedia.org/wiki/GeoJSON

A notable offspring of GeoJSON is TopoJSON, an extension of GeoJSON that encodes geospatial topology and that typically provides smaller file sizes.

https://en.wikipedia.org/wiki/GeoJSON

⇧ 地理情報に関する情報を持ったJSONということらしい。管理してる機関というものが無いってのが危険な臭いがしますな...

一応、

History

The GeoJSON format working group and discussion were begun in March 2007 and the format specification was finalized in June 2008.

In April 2015 the Internet Engineering Task Force has founded the Geographic JSON working group which released GeoJSON as RFC 7946 in August 2016.

https://en.wikipedia.org/wiki/GeoJSON

⇧ 2016年8月に、RFC 7946で定義はなされている模様。

経緯としては、

geojson.org

The GeoJSON Specification (RFC 7946)

In 2015, the Internet Engineering Task Force (IETF), in conjunction with the original specification authors, formed a GeoJSON WG to standardize GeoJSON. RFC 7946 was published in August 2016 and is the new standard specification of the GeoJSON format, replacing the 2008 GeoJSON specification.

https://en.wikipedia.org/wiki/GeoJSON

⇧ 2008年に仕様が決まってたんだけど、2016年8月に改めて仕様を策定したってことっぽい。

RFC 7946 によると、

datatracker.ietf.org

Applications that use this media type:  No known applications
   currently use this media type.  This media type is intended for
   GeoJSON applications currently using the "application/
   vnd.geo+json" or "application/json" media types, of which there
   are several categories: web mapping, geospatial databases,
   geographic data processing APIs, data analysis and storage
   services, and data dissemination.

https://datatracker.ietf.org/doc/html/rfc7946

⇧「MIMEタイプ (MIME type)」として、

どっちでも良いらしいが、「application/json」を指定しておけば良いんかな?

で、肝心の「GeoJSON」のフォーマットなのですが、

2. GeoJSON Objects

GeoJSON always consists of a single object. This object (referred to as the GeoJSON object below) represents a geometry, feature, or collection of features.

  • The GeoJSON object may have any number of members (name/value pairs).

  • The GeoJSON object must have a member with the name "type". This member's value is a string that determines the type of the GeoJSON object.

  • The value of the type member must be one of: "Point""MultiPoint""LineString""MultiLineString""Polygon""MultiPolygon""GeometryCollection""Feature", or "FeatureCollection". The case of the type member values must be as shown here.

  • A GeoJSON object may have an optional "crs" member, the value of which must be a coordinate reference system object (see 3. Coordinate Reference System Objects).

  • A GeoJSON object may have a "bbox" member, the value of which must be a bounding box array (see 4. Bounding Boxes).

https://web.archive.org/web/20160827120507/http://geojson.org/geojson-spec.html#definitions

⇧「type」というプロパティは必須で、

  1. Point
  2. MultiPoint
  3. LineString
  4. MultiLineString
  5. Polygon
  6. MultiPolygon
  7. GeometryCollection
  8. Feature
  9. FeatureCollection

⇧「type」というプロパティの値が9つあるらしいってことは何となく分かるものの、「GeoJSON」のプロパティの全量が見えてこない...

このあたり、もうちょっと整備されたドキュメントが欲しいですな...

JacksonがGeoJSONに対応していないらしい...

JSONからJavaのオブジェクトにデシリアライズする場合に、Jacksonというライブラリが利用されることが多いとは思うのだけど、JacksonはGeoJSONに対応していないらしい...。

Spring Web MVCで、リクエストにJSONを受け取る場合に、@RequestBodyを使うって情報がネットにあるのだけど、

docs.spring.io

@RequestBody

You can use the @RequestBody annotation to have the request body read and deserialized into an Object through an HttpMessageReader. 

https://docs.spring.io/spring-framework/reference/web/webflux/controller/ann-methods/requestbody.html

⇧@requestBodyに指定したJavaのオブジェクトのデシリアライズは、「HttpMessageReader」ってのが受け持ってるらしく、

Codecs

The spring-web and spring-core modules provide support for serializing and deserializing byte content to and from higher level objects through non-blocking I/O with Reactive Streams back pressure.

https://docs.spring.io/spring-framework/reference/web/webflux/reactive-spring.html#webflux-codecs

The spring-core module provides byte[], ByteBuffer, DataBuffer, Resource, and String encoder and decoder implementations. The spring-web module provides Jackson JSON, Jackson Smile, JAXB2, Protocol Buffers and other encoders and decoders along with web-only HTTP message reader and writer implementations for form data, multipart content, server-sent events, and others.

https://docs.spring.io/spring-framework/reference/web/webflux/reactive-spring.html#webflux-codecs

⇧ おそらく、@RequestBodyのデシリアライズにJacksonを使っていそうな気がする。

Spring Web MVCを使っている場合は、

HTTP message codecs

ServerCodecConfigurer provides a set of default readers and writers. You can use it to add more readers and writers, customize the default ones, or replace the default ones completely. For Jackson JSON and XML, consider using Jackson2ObjectMapperBuilder, which customizes Jackson’s default properties with the following ones:

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is disabled.
  • MapperFeature.DEFAULT_VIEW_INCLUSION is disabled.

It also automatically registers the following well-known modules if they are detected on the classpath:

  • jackson-datatype-joda: Support for Joda-Time types.
  • jackson-datatype-jsr310: Support for Java 8 Date and Time API types.
  • jackson-datatype-jdk8: Support for other Java 8 types, such as Optional.
  • jackson-module-kotlin: Support for Kotlin classes and data classes.

⇧ 追加の仕方は説明ないんね...

ちなみに、JSONからJavaオブジェクトへデシリアライズされる流れについては、

⇧ 上記サイト様の図がイメージしやすいかと。

話が脱線しましたが、Jacksonは、GeoJSONに対応していないらしいですと。

GeoJSONをデシリアライズしてSpring Data JPAPostgreSQLにINSERTしてみる

なので、GeoJSONをデシリアライズする部分は、自分で用意する必要がありますと。

ts0818.hatenablog.com

⇧ 前回の記事の「Spring Boot」プロジェクトに追加・修正をして、GeoJSONのデシリアライズに対応して、PostgreSQLへINSERTする処理を追加しました。

追加したファイルなど。

ソースコードなど。

■/geometry/build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.2'
	id 'io.spring.dependency-management' version '1.1.2'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'org.postgresql:postgresql'
	// https://mvnrepository.com/artifact/net.postgis/postgis-jdbc
    implementation group: 'net.postgis', name: 'postgis-jdbc', version: '2021.1.0'
	
	annotationProcessor 'org.projectlombok:lombok'
	// https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-spatial
    implementation group: 'org.hibernate.orm', name: 'hibernate-spatial', version: '6.3.0.CR1'
    // https://mvnrepository.com/artifact/com.graphhopper.external/jackson-datatype-jts
    implementation group: 'com.graphhopper.external', name: 'jackson-datatype-jts', version: '2.14'
    // https://mvnrepository.com/artifact/org.locationtech.jts/jts-core
    //simplementation group: 'org.locationtech.jts', name: 'jts-core', version: '1.19.0'
    implementation 'org.locationtech.jts.io:jts-io-common:1.18.2'
    // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.13.0'
    
 	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
    

■/geometry/src/main/resources/application.properties

# データベース接続情報
spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://ubuntuhost:5432/sample
spring.datasource.username=dev_web
spring.datasource.password=password

#spring.jpa.database-platform=org.hibernate.spatial.dialect.postgis.PostgisDialect
spring.jpa.show-sql=true
#spring.jpa.properties.hibernate.dialect=org.hibernate.spatial.dialect.postgis.PostgisDialect

logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
logging.level.tomcat=trace

#spring.main.banner-mode=off 
spring.output.ansi.enabled=ALWAYS


# 「空間参照系識別コード(SRID:Spatial Reference System IDentifier)」
srid.JGD2011.B.L=6668   

■/geometry/src/main/java/com/example/demo/config/JtsConfig.java

package com.example.demo.config;

import org.locationtech.jts.geom.Geometry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.example.demo.dto.flood.CustomGeometryDeserializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;

@Configuration
public class JtsConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Geometry.class, new CustomGeometryDeserializer());
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        mapper.registerModule(module);
        return mapper;
    }
}    

■/geometry/src/main/java/com/example/demo/constants/flood/FloodConstant.java

package com.example.demo.constants.flood;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@Data
public class FloodConstant {

	@Value("${srid.JGD2011.B.L}")
	public static String srid;
	
	public final static String FLOOD_ASSUMED_AREA_SHIZUOKA = "flood_assumed_area_shizuoka";
	public final static String FLOOD_ASSUMED_AREA_SHIZUOKA_SEQ = "flood_assumed_area_shizuoka_gid_seq";
}    

■/geometry/src/main/java/com/example/demo/dto/flood/CustomGeometryDeserializer.java

package com.example.demo.dto.flood;

import java.io.IOException;

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.geojson.GeoJsonReader;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

public class CustomGeometryDeserializer extends JsonDeserializer<geometry> {

    @Override
    public Geometry deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        GeoJsonReader reader = new GeoJsonReader();
        Geometry geometry = null;
        try {
            geometry = castTypeForGeometry(reader.read(node.toString()));
        } catch (ParseException e) {
            e.printStackTrace();
            // 例外処理
        }
        return geometry;
    }
    
    private static Geometry castTypeForGeometry (Geometry geometry) {
    	
    	if (geometry instanceof Point) {
    		return (Point)geometry;
    		
    	} else if (geometry instanceof MultiPoint) {
    		return (MultiPoint)geometry;
    		
    	} else if (geometry instanceof LineString) {
    		return (LineString)geometry;
    		
    	} else if (geometry instanceof LinearRing) {
    		return (LinearRing)geometry;
    		
    	} else if (geometry instanceof MultiLineString) {
    		return (MultiLineString)geometry;
    		
    	} else if (geometry instanceof Polygon) {
    		return (Polygon)geometry;
    		
    	} else if (geometry instanceof MultiPolygon) {
    		return (MultiPolygon)geometry;
    		
    	} else if (geometry instanceof GeometryCollection) {
    		return (GeometryCollection)geometry;
    		
    	}

    	return geometry;
    }
} 

■/geometry/src/main/java/com/example/demo/dto/flood/FloodGeoJsonDto.java

package com.example.demo.dto.flood;

import java.io.Serializable;

import org.locationtech.jts.geom.Geometry;
import org.springframework.stereotype.Component;

import com.bedatadriven.jackson.datatype.jts.serialization.GeometrySerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FloodGeoJsonDto implements Serializable {

	@JsonProperty(value="srid")
	private Integer srid;
	
	@JsonInclude(JsonInclude.Include.ALWAYS)
	@JsonProperty(value="geometryId")
	private Long geometryId;
	
	@JsonProperty(value="hazardousAreaClassification")
	private short hazardousAreaClassification;
	
	@JsonProperty(value="geometry")
	@JsonSerialize(using = GeometrySerializer.class)
	@JsonDeserialize(using = CustomGeometryDeserializer.class)
	//@JsonBackReference
	private Geometry geometryInfo;
}
   

■/geometry/src/main/java/com/example/demo/entity/flood/FloodAssumedAreaShizuoka.java

package com.example.demo.entity.flood;

import org.locationtech.jts.geom.Geometry;

import com.bedatadriven.jackson.datatype.jts.serialization.GeometryDeserializer;
import com.bedatadriven.jackson.datatype.jts.serialization.GeometrySerializer;
import com.example.demo.constants.flood.FloodConstant;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//import net.postgis.jdbc.geometry.Geometry;

@Entity
@Table(name="flood_assumed_area_shizuoka")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FloodAssumedAreaShizuoka {

	@Id
	@SequenceGenerator(name = FloodConstant.FLOOD_ASSUMED_AREA_SHIZUOKA
	, sequenceName = FloodConstant.FLOOD_ASSUMED_AREA_SHIZUOKA_SEQ
	, allocationSize = 1)
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name="gid")
	private Long geometryId;
	
	/** 危険区域区分 */
	@Column(name="a31_401")
	private short hazardousAreaClassification;
	
	@Column(name="geom", columnDefinition ="geometry(Multipolygon, 6668)")
	@JsonSerialize(using = GeometrySerializer.class)
	@JsonDeserialize(using = GeometryDeserializer.class)
	private Geometry geometry;
}
   

■/geometry/src/main/java/com/example/demo/repository/flood/FloodAssumedAreaShizuokaRepository.java

package com.example.demo.repository.flood;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.demo.entity.flood.FloodAssumedAreaShizuoka;

public interface FloodAssumedAreaShizuokaRepository extends JpaRepository<FloodAssumedAreaShizuoka, Long> {

}   

■/geometry/src/main/java/com/example/demo/service/flood/FloodAssumedAreaShizuokaService.java

package com.example.demo.service.flood;

import java.util.List;

import com.example.demo.entity.flood.FloodAssumedAreaShizuoka;

public interface FloodAssumedAreaShizuokaService {

	/**
	 * 全件検索
	 * @return
	 */
	public List<FloodAssumedAreaShizuoka> findAll();
	
	/**
	 * 検索(gid)
	 * @param gid geometry identifer
	 * @return
	 */
	public FloodAssumedAreaShizuoka findById(Long gid);
	
	public int insert(FloodAssumedAreaShizuoka floodAssumedAreaShizuoka);
}
  

■/geometry/src/main/java/com/example/demo/service/impl/flood/FloodAssumedAreaShizuokaServiceImpl.java

package com.example.demo.service.impl.flood;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Polygon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.dto.flood.FloodGeoJsonDto;
import com.example.demo.entity.flood.FloodAssumedAreaShizuoka;
import com.example.demo.repository.flood.FloodAssumedAreaShizuokaRepository;
import com.example.demo.service.flood.FloodAssumedAreaShizuokaService;

@Service
public class FloodAssumedAreaShizuokaServiceImpl implements FloodAssumedAreaShizuokaService {

	@Autowired
	private FloodAssumedAreaShizuokaRepository floodAssumedAreaShizuokaRepository;
	
	@Override
	public List<FloodAssumedAreaShizuoka> findAll() {
		// 全件検索
		List<FloodAssumedAreaShizuoka> floodAssumedAreaShizuoka = floodAssumedAreaShizuokaRepository.findAll();
		return floodAssumedAreaShizuoka;
	}

	@Override
	public FloodAssumedAreaShizuoka findById(Long gid) {
		// 検索(gid)
		Optional<FloodAssumedAreaShizuoka> floodAssumedAreaShizuoka = floodAssumedAreaShizuokaRepository.findById(gid);
		return floodAssumedAreaShizuoka.isPresent() ? floodAssumedAreaShizuoka.get() : null;
	}

	@Override
	public int insert(FloodAssumedAreaShizuoka floodAssumedAreaShizuoka) {
		// 登録
		floodAssumedAreaShizuokaRepository.save(floodAssumedAreaShizuoka);
		return 1;
	}

	public FloodAssumedAreaShizuoka createSaveData(FloodGeoJsonDto floodGeoJsonDto) {
        // input check
		if (Objects.isNull(floodGeoJsonDto) 
				|| Objects.isNull(floodGeoJsonDto.getGeometryInfo())
				|| Objects.isNull(floodGeoJsonDto.getGeometryInfo().getCoordinates())) {
			return null;
		}
		
		// create a MultiPolygon object
        GeometryFactory geometryFactory = new GeometryFactory();
        
        Coordinate[] coordinates = floodGeoJsonDto.getGeometryInfo().getCoordinates();
        LinearRing linearRing = geometryFactory.createLinearRing(coordinates);
        Polygon polygon = geometryFactory.createPolygon(linearRing);
        MultiPolygon multiPolygon = geometryFactory.createMultiPolygon(new Polygon[] {polygon});
        
        FloodAssumedAreaShizuoka floodAssumedAreaShizuoka = new FloodAssumedAreaShizuoka();
		multiPolygon.setSRID(
				Objects.nonNull(floodGeoJsonDto.getSrid()) 
				  ? floodGeoJsonDto.getSrid() 
				  : 6668
		);
        floodAssumedAreaShizuoka.setGeometry(multiPolygon);
        
        return floodAssumedAreaShizuoka;
	}
	
}
   

■/geometry/src/main/java/com/example/demo/controller/flood/FloodAssumedAreaController.java

package com.example.demo.controller.flood;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.dto.flood.FloodGeoJsonDto;
import com.example.demo.entity.flood.FloodAssumedAreaShizuoka;
import com.example.demo.service.impl.flood.FloodAssumedAreaShizuokaServiceImpl;

@RestController
@RequestMapping("floodAssumedArea")
public class FloodAssumedAreaController {

	@Autowired
	FloodAssumedAreaShizuokaServiceImpl floodAssumedAreaShizuokaServiceImpl;
	
	@GetMapping(value="/shizuoka")
	public List<FloodAssumedAreaShizuoka> serachFloodForShizuoka() {
		List<FloodAssumedAreaShizuoka> floodAssumedAreaShizuoka = floodAssumedAreaShizuokaServiceImpl.findAll();
		return floodAssumedAreaShizuoka;
	}
	
	@GetMapping(value="/shizuoka/{gid}")
	public FloodAssumedAreaShizuoka searchbyIdForShizuoka(@PathVariable("gid") Long gid) {
		FloodAssumedAreaShizuoka floodAssumedAreaShizuoka = floodAssumedAreaShizuokaServiceImpl.findById(gid);
		return floodAssumedAreaShizuoka;
	}
	
	@PostMapping(value="/shizuoka/save", consumes = "application/json")
	public int saveForShizuoka(@RequestBody FloodGeoJsonDto floodGeoJsonDto) {
		FloodAssumedAreaShizuoka  floodAssumedAreaShizuoka = floodAssumedAreaShizuokaServiceImpl.createSaveData(floodGeoJsonDto);
		int result = 0;
		if (Objects.nonNull(floodAssumedAreaShizuoka)) {
			result = floodAssumedAreaShizuokaServiceImpl.insert(floodAssumedAreaShizuoka);
		}
		return result;
	}
}

■/geometry/src/main/java/com/example/demo/GeometryApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GeometryApplication {

	public static void main(String[] args) {
		SpringApplication.run(GeometryApplication.class, args);
	}

}
 

 

で、「WSL 2(Windows SubSystem for Linux 2)」のUbuntu を起動しておく。(PostgreSQLに接続できるように)

そしたらば、Eclipseで「Spring Boot」プロジェクトを起動して、

Postmanからリクエストして、INSERTされました。

{
"srid": 6668
,"geometryId": null
,"hazardousAreaClassification":1
,"geometry":{ "type": "MultiPolygon",
  "coordinates": [
      [
        [
          [
            138.7328640796455,
            35.14438644107201
          ],
          [
            138.73281250000002,
            35.14395833335422
          ],
          [
            138.73271182134192,
            35.143912300393865
          ],
          [
            138.73256132323525,
            35.14390846901091
          ],
          [
            138.73239579992799,
            35.14391290687336
          ],
          [
            138.73228545494794,
            35.143917238751285
          ],
          [
            138.73218750000007,
            35.14395833335422
          ],
          [
            138.7321164773103,
            35.144425906965054
          ],
          [
            138.7322178234432,
            35.144419947497475
          ],
          [
            138.73247075234394,
            35.144406571804595
          ],
          [
            138.7327697723395,
            35.14439229575155
          ],
          [
            138.7328640796455,
            35.14438644107201
          ]
        ]
      ]
    ]
  }
}

⇧ GeoJSONとしてデシリアライズ対象の部分は、

{ "type": "MultiPolygon",
  "coordinates": [
      [
        [
          [
            138.7328640796455,
            35.14438644107201
          ],
          [
            138.73281250000002,
            35.14395833335422
          ],
          [
            138.73271182134192,
            35.143912300393865
          ],
          [
            138.73256132323525,
            35.14390846901091
          ],
          [
            138.73239579992799,
            35.14391290687336
          ],
          [
            138.73228545494794,
            35.143917238751285
          ],
          [
            138.73218750000007,
            35.14395833335422
          ],
          [
            138.7321164773103,
            35.144425906965054
          ],
          [
            138.7322178234432,
            35.144419947497475
          ],
          [
            138.73247075234394,
            35.144406571804595
          ],
          [
            138.7327697723395,
            35.14439229575155
          ],
          [
            138.7328640796455,
            35.14438644107201
          ]
        ]
      ]
    ]
  }    

になるかと。

テーブルを確認すると、1件のレコードが追加されていたので、JavaのプログラムでINSERTできています。

というわけで、Javaに限らないかもしれないですが、リクエストでGeoJSONが連携された場合に、デシリアライズに対応する必要があるようです。

何て言うか、Jackson側でGeoJSONに対応して欲しいんだが...

もしや、

spring.pleiades.io

spring.pleiades.io

Spring Framework側で、地理情報に対応してるんかね?

だけど、「org.springframework.data.geo」パッケージを見た感じ、MultiPolygonとか見当たらないし、そもそもGeoJSONとの関係がよく分からん...

何やら、

stackoverflow.com

⇧「geotools」ってライブラリを依存関係に追加して、GeoJSONをファイル形式で受け取る形でデシリアライズするって方法もあるとか。

う~む、地理情報、闇が深そう...

フロントエンドで必要としている情報が、

api-sdk.navitime.co.jp

⇧ 上記の通りだとすると、リクエストのインプット(サーバサイドに渡ってくるデータ)も変わるから、DTOの構造も変更する必要があるし、インプット・アウトプットのインターフェイスの仕様が決まらんと厳しいっすな...

毎度モヤモヤ感が半端ない...

今回はこのへんで。