Pretty big update

This commit is contained in:
Sebastien Deleuze
2016-03-16 00:00:43 +01:00
parent 20871a49a7
commit b9ad246ce8
13 changed files with 189 additions and 126 deletions

View File

@@ -24,22 +24,21 @@ open class DatabaseConfig {
@Bean
open fun init(userRepository: UserRepository, messageRepository: MessageRepository) = CommandLineRunner {
val jbauer = User("jbauer", "Jack", "Bauer")
val cobrian = User("cobrian", "Chloe", "O'Brian")
val kbauer = User("kbauer", "Kim", "Bauer")
val dpalmer = User("dpalmer", "David", "Palmer")
val mdessler = User("mdessler", "Michelle", "Dessler")
val swhite = User("swhite", "Skyler", "White")
val jpinkman = User("jpinkman", "Jesse", "Pinkman")
val walter = User("wwhite", "Walter", "White")
val sgoodman = User("sgoodman", "Saul", "Goodman")
if(userRepository.count() == 0L) {
userRepository.create(jbauer)
userRepository.create(cobrian)
userRepository.create(kbauer)
userRepository.create(dpalmer)
userRepository.create(mdessler)
userRepository.create(swhite)
userRepository.create(jpinkman)
userRepository.create(walter)
userRepository.create(sgoodman)
}
if(messageRepository.count() == 0L) {
messageRepository.create(Message("This is a test!", jbauer))
messageRepository.create(Message("This is a test!", swhite.userName))
}
}

View File

@@ -5,7 +5,7 @@ import org.springframework.data.annotation.Id
data class Message(
var content: String,
var author: User,
var author: String,
var location: Point? = null,
@Id var id: Long? = null
)

View File

@@ -0,0 +1,11 @@
package io.spring.messenger.domain
import org.postgis.Point
import org.springframework.data.annotation.Id
data class MessageWithAuthor(
var content: String,
var author: User,
var location: Point? = null,
@Id var id: Long? = null
)

View File

@@ -9,11 +9,9 @@ import org.postgis.PGgeometry
import org.postgis.Point
import org.springframework.jdbc.core.RowMapper
import org.springframework.stereotype.Repository
import java.sql.ResultSet
@Repository
open class MessageRepository : JdbcRepository<Message, Long>(MessageRowMapper(), MessageRowUnmapper(),
TableDescription("messages", "messages JOIN users ON messages.author = users.user_name", "id")) {
open class MessageRepository : JdbcRepository<Message, Long>(mapper(), unmapper(), TableDescription("messages", "id")) {
open fun findByBoundingBox(box: PGbox2d): List<Message>
= jdbcOperations.query("""SELECT * FROM ${table.name}
@@ -21,34 +19,26 @@ open class MessageRepository : JdbcRepository<Message, Long>(MessageRowMapper(),
ST_MakeEnvelope(${box.llb.x}, ${box.llb.y}, ${box.urt.x}, ${box.urt.y}
, 4326)""", rowMapper)
class MessageRowMapper : RowMapper<Message> {
val userRowMapper = UserRepository.UserRowMaper()
override fun mapRow(rs: ResultSet, rowNum: Int)
= Message(
rs.getString("content"),
userRowMapper.mapRow(rs, rowNum),
(rs.getObject("location") as PGgeometry?)?.geometry as Point?,
rs.getLong("id"))
}
class MessageRowUnmapper : RowUnmapper<Message> {
override fun mapColumns(message: Message): Map<String, Any?> {
val map = mutableMapOf(
Pair("id", message.id),
Pair("content", message.content),
Pair("author", message.author?.userName)
)
if (message.location != null) {
message.location!!.srid = 4326
map["location"] = PGgeometry(message.location)
}
return map
}
}
override fun <S : Message> postCreate(entity: S, generatedId: Number?): S {
if (generatedId != null) entity.id = generatedId.toLong()
return entity
}
}
private fun mapper() = RowMapper<Message> {
rs, rowNum -> Message(
rs.getString("content"),
rs.getString("author"),
(rs.getObject("location") as PGgeometry?)?.geometry as Point?,
rs.getLong("id"))
}
private fun unmapper() = RowUnmapper<Message> {
m ->
val map = mutableMapOf(Pair("id", m.id), Pair("content", m.content), Pair("author", m.author))
if (m.location != null) {
m.location!!.srid = 4326
map["location"] = PGgeometry(m.location)
}
map
}

View File

@@ -8,10 +8,9 @@ import org.postgis.PGgeometry
import org.postgis.Point
import org.springframework.jdbc.core.RowMapper
import org.springframework.stereotype.Repository
import java.sql.ResultSet
@Repository
open class UserRepository : JdbcRepository<User, String>(UserRowMaper(), UserRowUnmapper(), "\"users\"", "user_name") {
open class UserRepository : JdbcRepository<User, String>(mapper(), unmapper(), "\"users\"", "user_name") {
open fun updateLocation(userName:String, location: Point): Unit {
location.srid = 4326
@@ -24,29 +23,25 @@ open class UserRepository : JdbcRepository<User, String>(UserRowMaper(), UserRow
ST_MakeEnvelope(${box.llb.x}, ${box.llb.y}, ${box.urt.x}, ${box.urt.y}
, 4326)""", rowMapper)
class UserRowMaper : RowMapper<User> {
override fun mapRow(rs: ResultSet, rowNum: Int)
= User(
rs.getString("user_name"),
rs.getString("first_name"),
rs.getString("last_name"),
(rs.getObject("location") as PGgeometry?)?.geometry as Point?)
}
class UserRowUnmapper() : RowUnmapper<User> {
override fun mapColumns(user: User): Map<String, Any> {
val map = mutableMapOf<String, Any>(
Pair("user_name", user.userName),
Pair("first_name", user.firstName),
Pair("last_name", user.lastName))
if (user.location != null) {
user.location!!.srid = 4326
map["location"] = PGgeometry(user.location)
}
return map;
}
}
}
private fun mapper() = RowMapper<User> {
rs, rowNum -> User(
rs.getString("user_name"),
rs.getString("first_name"),
rs.getString("last_name"),
(rs.getObject("location") as PGgeometry?)?.geometry as Point?)
}
private fun unmapper() = RowUnmapper<User> {
user ->
val map = mutableMapOf<String, Any>(
Pair("user_name", user.userName),
Pair("first_name", user.firstName),
Pair("last_name", user.lastName))
if (user.location != null) {
user.location!!.srid = 4326
map["location"] = PGgeometry(user.location)
}
map;
}

View File

@@ -1,22 +1,32 @@
package io.spring.messenger.web
import io.spring.messenger.domain.Message
import io.spring.messenger.repository.MessageRepository
import org.postgis.PGbox2d
import org.postgis.Point
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.*
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter
@RestController
@RequestMapping("/message")
class MessageController @Autowired constructor(val repository: MessageRepository) {
val broadcaster = SseBroadcaster()
@PostMapping
fun create(@RequestBody message: Message) = repository.create(message)
@GetMapping
fun findMessages() = repository.findAll()
/**
* {@code box} parameter should use this format: xmin%20ymin,xmax%20ymax
*/
@GetMapping("/bbox/{box}")
fun findByBoundingBox(@PathVariable box:String)
= repository.findByBoundingBox(PGbox2d("BOX($box)"))
@GetMapping("/bbox/{xMin},{yMin},{xMax},{yMax}")
fun findByBoundingBox(@PathVariable userName:String, @PathVariable xMin:Double,
@PathVariable yMin:Double, @PathVariable xMax:Double, @PathVariable yMax:Double)
= repository.findByBoundingBox(PGbox2d(Point(xMin, yMin), Point(xMax, yMax)))
@GetMapping("/subscribe")
fun subscribe(): SseEmitter {
return broadcaster.subscribe();
}
}

View File

@@ -12,20 +12,18 @@ import org.springframework.web.bind.annotation.*
class UserController @Autowired constructor(val repository: UserRepository) {
@GetMapping
fun findUsers() = repository.findAll()
fun findAll() = repository.findAll()
@PostMapping
fun createUser(@RequestBody user: User) = repository.create(user)
fun create(@RequestBody user: User) = repository.create(user)
@PutMapping("/{userName}/location") @ResponseStatus(NO_CONTENT)
fun updateLocation(@PathVariable userName:String, @RequestBody location: Point)
= repository.updateLocation(userName, location)
@PutMapping("/{userName}/location/{x},{y}") @ResponseStatus(NO_CONTENT)
fun updateLocation(@PathVariable userName:String, @PathVariable x: Double, @PathVariable y: Double)
= repository.updateLocation(userName, Point(x, y))
/**
* {@code box} parameter should use this format: xmin%20ymin,xmax%20ymax
*/
@GetMapping("/bbox/{box}")
fun findByBoundingBox(@PathVariable userName:String, @PathVariable box:String)
= repository.findByBoundingBox(PGbox2d("BOX($box)"))
@GetMapping("/bbox/{xMin},{yMin},{xMax},{yMax}")
fun findByBoundingBox(@PathVariable userName:String, @PathVariable xMin:Double,
@PathVariable yMin:Double, @PathVariable xMax:Double, @PathVariable yMax:Double)
= repository.findByBoundingBox(PGbox2d(Point(xMin, yMin), Point(xMax, yMax)))
}

View File

@@ -1,3 +1,6 @@
logging:
level:
org.springframework.web: DEBUG
spring:
datasource:
platform: "postgis"

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

View File

@@ -1,21 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Localized OpenStreetMap</title>
<title>Geospatial Messenger</title>
<link rel="stylesheet" href="http://openlayers.org/en/v3.14.2/css/ol.css" type="text/css">
<link rel="stylesheet" href="style.css">
<script src="http://openlayers.org/en/v3.14.2/build/ol.js"></script>
<script src="http://code.jquery.com/jquery-2.2.1.min.js"></script>
<script src="http://www.appelsiini.net/download/jquery.jeditable.mini.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<div id="info" style="display: none;"></div>
<p>
position accuracy : <code id="accuracy"></code>&nbsp;&nbsp;
altitude : <code id="altitude"></code>&nbsp;&nbsp;
altitude accuracy : <code id="altitudeAccuracy"></code>&nbsp;&nbsp;
heading : <code id="heading"></code>&nbsp;&nbsp;
speed : <code id="speed"></code>
</p>
<span id="status">&nbsp;0 selected features</span>
<div id="popup" class="ol-popup">
<div id="popup-content"></div>
</div>
<script src="map.js"></script>
</body>
</html>

View File

@@ -1,61 +1,74 @@
function el(id) {
return document.getElementById(id);
}
var userName = "swhite"; // Currently hardcoded
// #################################### Map ####################################
var view = new ol.View({
zoom: 8
});
var geolocation = new ol.Geolocation({
projection: view.getProjection()
});
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var overlay = new ol.Overlay(({
element: container, autoPan: true, autoPanAnimation: {
duration: 250
}
}));
var map = new ol.Map({
layers: [new ol.layer.Tile({
source: new ol.source.OSM()
})], target: 'map', controls: ol.control.defaults({
attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
source: new ol.source.MapQuest({layer: 'osm'})
})], target: 'map', overlays: [overlay], controls: ol.control.defaults({
attributionOptions: ({
collapsible: false
})
}), view: view
});
geolocation.on('change', function () {
el('accuracy').innerText = geolocation.getAccuracy() + ' [m]';
el('altitude').innerText = geolocation.getAltitude() + ' [m]';
el('altitudeAccuracy').innerText = geolocation.getAltitudeAccuracy() + ' [m]';
el('heading').innerText = geolocation.getHeading() + ' [rad]';
el('speed').innerText = geolocation.getSpeed() + ' [m/s]';
});
// ################################# Geolocation #################################
// handle geolocation error.
var geolocation = new ol.Geolocation({
projection: view.getProjection()
});
geolocation.on('error', function (error) {
var info = document.getElementById('info');
info.innerHTML = error.message;
info.style.display = '';
alert(error.message);
});
var positionFeature = new ol.Feature();
positionFeature.setStyle(new ol.style.Style({
image: new ol.style.Icon({src: "horse.png", scale: 0.25})
}));
var centerDefined = false;
geolocation.on('change:position', function () {
var coordinates = geolocation.getPosition();
$.ajax({
url: "/user/" + userName + "swhite/location/" + coordinates[0] + "," + coordinates[1], type: "PUT"
});
if (!centerDefined) {
view.setCenter(coordinates);
centerDefined = true;
}
positionFeature.setGeometry(coordinates ? new ol.geom.Point(coordinates) : null);
view.setCenter(coordinates);
});
new ol.layer.Vector({
map: map, source: new ol.source.Vector({
features: [positionFeature]
})
});
geolocation.setTracking(true);
var select = new ol.interaction.Select();
map.addInteraction(select);
select.on('select', function (e) {
el('status').innerHTML = '&nbsp;' + e.target.getFeatures().getLength() + ' selected features';
// ################################# Popup #################################
map.on('singleclick', function (evt) {
var coordinate = evt.coordinate;
var hdms = ol.coordinate.toStringHDMS(ol.proj.transform(coordinate, 'EPSG:3857', 'EPSG:4326'));
var message = '<p>You clicked here:</p><code>' + hdms + '</code>';
content.innerHTML = message;
overlay.setPosition(coordinate);
$.ajax({
method: "POST",
url: "/message",
data: JSON.stringify({content: message, author: userName, location: {type: "Point", coordinates:[coordinate[0],coordinate[1]]}}),
contentType:"application/json; charset=utf-8",
dataType:"json"
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 KiB

View File

@@ -0,0 +1,47 @@
.ol-popup {
position: absolute;
background-color: white;
-webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
min-width: 280px;
}
.ol-popup:after, .ol-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
}
.ol-popup-closer:after {
content: "✖";
}