[JAVA] Prise en compte du SIG et du Mappeur O / R

Données SIG MySQL et JPA

Essayez d'accéder aux données MySQL GIS via JPA de JavaEE (WildFly). Cette fois, j'utiliserai les informations de "latitude / longitude" qui sont considérées comme étant le plus souvent utilisées. Les champs de données SIG sont stockés dans leur propre binaire, alors comment y accéder ...

Essayez-le

Créons un mécanisme pour stocker les informations de position (latitude / longitude) via RestAPI et pour le calcul de distance à l'aide de la fonction SIG. En particulier 1: informations de latitude / longitude POST avec ID de l'API Rest 2: Enregistrer les données SIG pour l'ID dans MySQL 3: GET en spécifiant l'ID de l'API Rest 4: Obtenez des enregistrements de MySQL 5: Calculez la distance à vous-même en utilisant la fonction SIG de MySQL 6: Tri et sortie par ordre croissant de distance Je vais essayer de mettre en œuvre le processus. (L'environnement est WildFly14 + MySQL8.0.13 + Connector / J8.0.13)

Génération de données SIG à partir de la latitude / longitude

Les données SIG sont de type octet [] sur Java. (Binaire d'origine ...) La conversion de latitude / longitude en binaire peut être faite avec la fonction ST_GeomFromText ('POINT (latitude longitudinale)'), mais il est difficile d'appeler createNativeQuery pour obtenir le binaire et l'enregistrer en tant que setter, donc GenerateColumn Je vais le générer avec.

createtable.sql


CREATE TABLE `position` (
  `id` varchar(64) NOT NULL,
  `longitude` varchar(256) DEFAULT NULL,
  `latitude` varchar(256) DEFAULT NULL,
  `location` point GENERATED ALWAYS AS (st_geomfromtext(concat(_utf8mb4'POINT(',`longitude`,_utf8mb4' ',`latitude`,_utf8mb4')'))) STORED,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

L'entité du côté Java pour cela est

Position.java


@Entity
@Table(name="position")
public class Position implements Serializable {
	@Id
	private String id;
	private String latitude;
	private String longitude;
	@Column(
		name = "location",
		insertable = false,
		updatable = false
	)
	private byte[] location;
	private static final long serialVersionUID = 1L;

	//Ci-dessous Getter et Setter
	//Cependant, l'emplacement est uniquement Getter
}

Ce sera. Il est nécessaire de traiter la colonne générée pour qu'elle ne soit pas écrite au moment de l'insertion / mise à jour.

Le code qui reçoit POST avec JAX-RS est

API.java


@RequestScoped
@Path("/api")
@Produces("application/json")
@Consumes("application/json")
public class API {
	@PersistenceContext(unitName = "geotest")
	private EntityManager em;

	@POST
	@Path("{ID}")
	@Transactional
	public Response setPosition(@PathParam("ID") String ID, Point point) {
		Position position = new Position();
		position.setId(ID);
		position.setLatitude(point.getLatitude());
		position.setLongitude(point.getLongitude());
		em.persist(position);
		return Response.ok().status(201).build();
	}
}

Point.java


public class Point {
	private String Latitude;
	private String Longitude;

	//Ci-dessous Getter et Setter
}

POSTER JSON sur http: // .... / {ID} sauvegardera les données SIG pour {ID}.

Utilisation de la fonction (fonction) de MySQL GIS

Ce serait bien s'il y avait une version Java de Boost.Geometry, mais il n'y a rien qui n'y soit pas ... J'ai donc décidé d'utiliser la fonction SIG de MySQL d'EntityManager via createNativeQuery.

GeoPoint.java


@Stateless
public class GeoPoint {
	@PersistenceContext(unitName = "geotest")
	private EntityManager em;

	public String distance(byte[] pt1, byte[] pt2) {
		return String.valueOf(em.createNativeQuery("select ST_Distance_Sphere(unhex('" + tohex(pt1) + "'), unhex('" + tohex(pt2) + "'))").getSingleResult());
	}

	private String tohex(byte[] bin) {
		String p = "";
		for(int i=0; i<bin.length; i++) {
			p = p + String.format("%02x", bin[i]);
		}
		return p;
	}
}

Je fais beaucoup de travail pour interroger et taper des données binaires, mais il peut y avoir un moyen plus propre. Quoi qu'il en soit, en injectant avec EJB, le résultat de la fonction ST_Distance_Sphere peut être reçu sous forme de chaîne de caractères.

API pour obtenir une liste triée par distance

Ajoutez la méthode GET pour obtenir la liste dans l'API d'inscription (API.java) créée précédemment.

API.java


@RequestScoped
@Path("/api")
@Produces("application/json")
@Consumes("application/json")
public class API {

	@PersistenceContext(unitName = "geotest")
	private EntityManager em;

	@EJB
	private GeoPoint geoPoint;

	@GET
	@Path("{ID}")
	public Response getPosition(@PathParam("ID") String ID) {
		Position mypos = em.find(Position.class, ID);
		List<Position> pos = em.createQuery("select p from Position p", Position.class).getResultList();
		List <Result> results = pos.stream()
				.filter(p -> !p.getId().equals(mypos.getId()))
				.map(p -> {
					Result result = new Result();
					result.setID(p.getId());
					result.setDistance(Double.parseDouble(geoPoint.distance(mypos.getLocation(), p.getLocation())));
					return result;
				})
				.sorted(comparing(Result::getDistance))
				.collect(Collectors.toList());
		return Response.ok(results).build();
	}

	@POST
	@Path("{ID}")
	@Transactional
	public Response setPosition(@PathParam("ID") String ID, Point point) {
		Position position = new Position();
		position.setId(ID);
		position.setLatitude(point.getLatitude());
		position.setLongitude(point.getLongitude());
		em.persist(position);
		return Response.ok().status(201).build();
	}

}

Result.java


public class Result {
	private String ID;
	private Double Distance;

	//Ci-dessous Getter et Setter
}

result.setDistance (Double.parseDouble (geoPoint.distance (mypos.getLocation (), p.getLocation ()))); une partie est le traitement lié au SIG.

Les données binaires SIG sont affectées à l'EJB de calcul de distance geoPoint.distance (byte [] pos1, byte [] pos2) créé précédemment avec mypos.getLocation () et p.getLocation (). ..

J'ai essayé d'enregistrer la latitude et la longitude de la station via l'API. キャプチャ.JPG

D'un autre côté, si vous lancez la station http: // ..... / Osaka et la méthode Get, ce qui suit sera retourné.

result.json


[
	{
		"ID": "Gare de Sannomiya",
		"distance": 26586.663958186175
	},
	{
		"ID": "Gare de Kyoto",
		"distance": 39434.1794831947
	},
	{
		"ID": "Gare de Nagoya",
		"distance": 134598.65725231185
	}
]

Il est trié par ordre de proximité et renvoyé ♪

Résumé

Cependant, si vous utilisez la fonction SIG pour effectuer une grande quantité de calculs, la surcharge liée à l'émission des requêtes peut augmenter, il peut donc être nécessaire de créer une fonction StoredFunction côté MySQL et de l'appeler. N'existe-t-il pas une version Java de Boost.Geometry ... www

Recommended Posts

Prise en compte du SIG et du Mappeur O / R
Compréhension étape par étape de la cartographie O / R