Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
A D D I N G G E O S P AT I A L F E AT U R E S
T O A J A V A W E B A P P
C O D E O N E 2 0 1 8
Matti Tahvonen
Web pages from 1994
Web apps from 1996 (JavaScript, Perl, PHP…)
Java web apps from 2006 (Vaadin R&D + helping our
customers)
Since 2014, mostly DevRel/marketing as daily job, but in
Vaadin that means that IDE is open all the time :-)
Orienteering, sailing, hiking, mountain biking, hunting…
OSS ADVOCATE
@mattitahvonen
W H Y A M I H E R E ?
- 1985 Started orienteering (endurance sport)
- 1994 I got interested about WWW
- 2006 Joined a Java company as our first JS developer
- 2009 OpenLayers wrapper for Vaadin (Java)
- 2018 I can still do JS & browser magic, but prefer Java & JVM
T O D A Y
- You’ll realise how easy it is to add small (but professional)
GIS features to your existing business apps.
- Full stack Sailing Course Planner example (Open Source)
- Introduction to a set of handy GIS libraries and some
alternatives
SCP - SAILING COURSE PLANNER
OSS A PP TO PL AN AND SET UP SAILING COMPETITIONS
Adding geospatial features to a java web app
A R C H I T E C T U R E
A R C H I T E C T U R E O V E R V I E W
PosgreSQL
PostGIS
JVM
Spring Boot, JTS,
Hibernate (spatial),
Geotools, Vaadin,
Leaflet
Mobile web app
for course set-up
Desktop/Tablet
optimised web app
for course design
PostgreSQL + PostGIS
Open Source
Excellent support for spatial indexes and
functions
Alternatives:
Oracle, MS SQL, MySQL/MariaSQL, GeoDB (H2 db
extension)…
Many NoSQL databases, like also support spatial
indexes and queries
Do you need spatial aware persistency solution at
all?
Persistency
Sailing Course Planner architecture
Database with spatial extensions?
With large datasets JVM can’t handle all in
memory. “Select entities in this viewport only”,
“Select entities overlapping given polygon” etc…
Datatypes, geospatial functions/operators and
indexes.
Support for somewhat standard SQL extensions
varies a lot among databases.
For some use cases, you don’t need one. Just
store features as blobs, or points in lat/lon
columns.
Persistency
Sailing Course Planner architecture
CREATE EXTENSION postgis;
CREATE TABLE geometries (name varchar, geom geometry);
INSERT INTO geometries VALUES
('Point', 'POINT(0 0)'),
('Linestring', 'LINESTRING(0 0, 1 1, 2 1, 2 2)'),
('Polygon', 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
('PolygonWithHole', 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 …
('Collection', 'GEOMETRYCOLLECTION(POINT(2 0),POLYGON(( …
SELECT name, ST_AsText(geom) FROM geometries;
SELECT name, ST_AsText(geom) FROM geometries WHERE
ST_Contains(
ST_GeomFromText('POLYGON((-1 -1,1 -1,1 1,-1 1,-1 -1))')
, geom
);
name | st_astext
-----------------+---------------------------------
Point | POINT(0 0)
Linestring | LINESTRING(0 0,1 1,2 1,2 2)
Polygon | POLYGON((0 0,1 0,1 1,0 1,0 0))
PolygonWithHole | POLYGON((0 0,10 0,10 10,0 10,0 …
Collection | GEOMETRYCOLLECTION(POINT(2 0), …
(5 rows)
name | st_astext
---------+--------------------------------
Point | POINT(0 0)
Polygon | POLYGON((0 0,1 0,1 1,0 1,0 0))
(2 rows)
Example derived from the PostGIS tutorial
JVM libraries in use
Spring Boot - application framework
JTS Java Topology Suite - datatypes for domain
model and basic planar math
Spring Data JPA/Hibernate (spatial)- object
relational mapping and database connectivity
Geotools - tools to handle geospatial data in Java
JSR 363, Units of Measurement - conversion of
distances to weird units
Vaadin - the plain Java UI library for web apps
Leaflet - a slippy map web component
JVM
Sailing Course Planner architecture
Spring Boot
An application framework, makes easy to
develop and deploy web apps.
Alternatives
Plain servlet application, Java EE
Spring Boot
Sailing Course Planner architecture
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Route
public static class MainView extends VerticalLayout {
public MainView() {
add(new Span("Hello World!"));
}
}
}
JTS aka Java Topology Suite
The most commonly used library in Java apps
handling geographical information.
Object model (Point, LineString, Polygon…) for
planar linear geometry and a set of fundamental
geometric functions.
C++ port (GEOM) powers PostGIS, QGIS, Google
Earth etc..
Moving to Eclipse (LocationTech project)
Alternatives
Geolatte, Apache SIS, Spatial4j
JTS
Sailing Course Planner architecture
GeometryFactory geometryFactory = new GeometryFactory();
Point point = geometryFactory.createPoint(
new Coordinate(1, 1)
);
LinearRing linearRing = geometryFactory.createLinearRing(
new Coordinate[]{
new Coordinate(0, 0),
new Coordinate(0, 2),
new Coordinate(2, 2),
new Coordinate(2, 0),
new Coordinate(0, 0)
}
);
boolean contains = linearRing.contains(point); // true
Hibernate (spatial)
Hibernate 5 introduced an extension for
geographic types (JTS and Geolatte)
Previously a community extension
Provides also some spatial functions to HQL and
criteria API.
Alternatives
QueryDSL has a spatial extension
EclipseLink (JPA implementation) contains some
custom support for Oracle databases
Plain JDBC (data comes in/out as WKT)
JPA
Sailing Course Planner architecture
@Entity
public class SpatialEvent extends AbstractEntity {
private String title;
@Temporal(TemporalType.DATE)
private Date date;
//@Column(columnDefinition = "POINT") // this type is known by MySQL
@Column(columnDefinition = "geometry")
private Point location;
// @Column(columnDefinition = "POLYGON") // this type is known by MySQL
@Column(columnDefinition = "geometry")
private LineString route;
//…
}
//Select only entities within current viewport
Polygon viewPort = geometryFactory.createPolygon( coordinates );
SpatialEvent event = entityManager.createQuery(
"select e " +
"from SpatialEvent e " +
"where within(e.location, :viewPort) = true", SpatialEvent.class)
.setParameter("window", viewPort)
.getSingleResult();
}
Spring Data JPA
Repository pattern for JPA
No special support for geometries, but one can
define SQL/JPQL/HQL explicitly.
Alternatives
DeltaSpike Data (CDI)
JPA
Sailing Course Planner architecture
public interface SailingCourseRepository
extends JpaRepository<SailingCourse,Long> {
// findAll, count, save, delete, findBy(Example example) etc via super interface
@EntityGraph(attributePaths = {"mainBuoys", "helperBuoys", "coursePoints", "adminEmails"})
public SailingCourse findOneWithDetailsById(Long id);
@EntityGraph(attributePaths = {"mainBuoys", "helperBuoys", "coursePoints", "adminEmails"})
public SailingCourse findByUuid(String uuid);
@Query("SELECT DISTINCT s FROM SailingCourse s JOIN s.adminEmails e WHERE e = :email")
public List<SailingCourse> retrieveByEmail(@Param(“email") String email);
}
Geotools: tools for Java GIS apps
Geometry support provided by Java Topology
Suite (JTS)
Read/write many raster and vector formats
Tools for more complex analyses, like geodetic
calculations
Swing components
Alternatives
Spatial4j, Geolatte, Apache SIS
Geotools
Sailing Course Planner architecture
double angle = 2 * Math.PI * degrees / 360;
Point centroid = getCentroid();
AffineTransformation at = new AffineTransformation()
.setToRotation(angle, centroid.getX(), centroid.getY());
for (MainBuoy mb : getMainBuoys()) {
Geometry transformed = at.transform(mb.getLocation());
mb.setLocation((Point) transformed);
}
for (HelperBuoy mb : getHelperBuoys()) {
Geometry transformed = at.transform(mb.getLocation());
mb.setLocation((Point) transformed);
}
Why Geotools?
Transformation of WSG84 coordinates using JTS
Don’t do this with geodesic coordinates!
gc = new GeodeticCalculator()
gc.setStartingGeographicPoint(
lastKnownPoint.getLon(),
lastKnownPoint.getLat()
);
gc.setDestinationPosition(
JTS.toDirectPosition(
targetBuoy.getLocation().getCoordinate(), crs)
);
int azimuth = (int) gc.getAzimuth();
if (azimuth < 0) {
// convert to "compass angle"
azimuth = azimuth + 360;
}
double orthodromicDistance = gc.getOrthodromicDistance();
C O U N T R I E S N O T U S I N G
M E T R I C S Y S T E M
Units of Measurement API
A new standard Java API for presenting and
handling units of measurement.
Libraries by default use SI units
Not that tightly related to geospatial features, but
you quite often need custom presentation for
that one nation (and sailors).
Alternatives
Dig constants from wikipedia and build your own
conversion methods
JSR 363 UOM
Sailing Course Planner architecture
public static String formatDistance(double meters) {
LengthAmount l = new LengthAmount(meters, SI.METRE);
Quantity to = l.to(NonSI.NAUTICAL_MILE);
return String.format("%d m, %.2f nm", (int) meters, to.getValue());
}
Vaadin - Java UI framework to build
modern web apps
Creates XHR/WebSocket powered SPAa even
with plain Java code, without web expertise.
For both desktop and mobile devices.
Extensible and has 700+ community extensions.
Has good slippy map widgets based on Leaflet,
OpenLayers and Google Maps
Alternatives
JSF, Wicket, React (JS), Angular (JS), Vue (JS)
Vaadin
Sailing Course Planner architecture
new VerticalLayout(
new Button("I'm a button"),
new DatePicker("Select a date"),
new ComboBox <>("Select an option"),
new Span("I'm a <span>")
);
Slim OSS alternative to Google Maps
Small well performing core
Good support for different base maps
Excellent for both desktop and mobile devices
Extensible, lot of community extensions
Alternatives
OpenLayers, Google Maps (license!!), Mapbox GL
JS (License)
Leaflet
Sailing Course Planner architecture
var map = L.map(‘map’)
.setView([-122.3, 37.7], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
L.marker([-122.3, 37.7]).addTo(map);
Java/Vaadin API for LeafletJS
Wraps LeafletJS with Java API
Trivial to integrate to other JVM technologies, no
REST APIs or data conversions needed
Contains Vaadin fields to directly edit JTS data
types -> developer productivity
Extension for editing, heat map, marker cluster…
Alternatives
Vaadin OpenLayers integration, JSF integrations,
REST API + client side JS code (additional
complexity, but access to all low level details)
v-leaflet
Sailing Course Planner architecture
LMap map = new LMap();
map.addLayer(new LOpenStreetMapLayer());
map.addLayer(new LMarker(-122.3, 37.7));
private TextField title = new TextField();
private DateField date = new DateField();
private PointField location = new PointField();
private LineStringField route = new LineStringField();
public void bindEntityToForm(SpatialEvent entity) {
layout.add(title, date, location, route);
// naming based binding to properties in SpatialEvent
BeanValidationBinder<SpatialEvent> binder =
new BeanValidationBinder<>(SpatialEvent.class);
binder.bindInstanceFields(this);
binder.setBean(entity);
}
KEY TAKEAWAYS
ADDING GEOS PATI AL FEATURES TO A JAVA WEB APP
D E V E L O P I N G B A S I C G E O S P AT I A L F E AT U R E S I S
E A S I E R T H A N YO U T H I N K
J A V A E C O S Y S T E M H A S A L O T O F H A N D Y
L I B R A R I E S F O R G I S
V A A D I N + L E A F L E T I S P R O B A B L Y T H E E A S I E S T
S O L U T I O N F O R J A V A D E V E L O P E R T O B U I L D U I
Vaadin booth in expo area
github.com/mstahv/scp
vaadin.com/forum
I’ll be there for more questions today & tomorrow.
Source code for the Sailing Course Planner example
A good place for Vaadin (+ geospatial) related questions
Vaadin BOF session today 20:30
BOF5640 in room 2010, today 08:30 PM
T H A N K S
@ M A T T I T A H V O N E N
Q U E S T I O N S ?
A D D I N G G E O S P AT I A L F E AT U R E S
T O A J A V A W E B A P P

More Related Content

Adding geospatial features to a java web app

  • 1. A D D I N G G E O S P AT I A L F E AT U R E S T O A J A V A W E B A P P C O D E O N E 2 0 1 8
  • 2. Matti Tahvonen Web pages from 1994 Web apps from 1996 (JavaScript, Perl, PHP…) Java web apps from 2006 (Vaadin R&D + helping our customers) Since 2014, mostly DevRel/marketing as daily job, but in Vaadin that means that IDE is open all the time :-) Orienteering, sailing, hiking, mountain biking, hunting… OSS ADVOCATE @mattitahvonen
  • 3. W H Y A M I H E R E ? - 1985 Started orienteering (endurance sport) - 1994 I got interested about WWW - 2006 Joined a Java company as our first JS developer - 2009 OpenLayers wrapper for Vaadin (Java) - 2018 I can still do JS & browser magic, but prefer Java & JVM
  • 4. T O D A Y - You’ll realise how easy it is to add small (but professional) GIS features to your existing business apps. - Full stack Sailing Course Planner example (Open Source) - Introduction to a set of handy GIS libraries and some alternatives
  • 5. SCP - SAILING COURSE PLANNER OSS A PP TO PL AN AND SET UP SAILING COMPETITIONS
  • 7. A R C H I T E C T U R E
  • 8. A R C H I T E C T U R E O V E R V I E W PosgreSQL PostGIS JVM Spring Boot, JTS, Hibernate (spatial), Geotools, Vaadin, Leaflet Mobile web app for course set-up Desktop/Tablet optimised web app for course design
  • 9. PostgreSQL + PostGIS Open Source Excellent support for spatial indexes and functions Alternatives: Oracle, MS SQL, MySQL/MariaSQL, GeoDB (H2 db extension)… Many NoSQL databases, like also support spatial indexes and queries Do you need spatial aware persistency solution at all? Persistency Sailing Course Planner architecture
  • 10. Database with spatial extensions? With large datasets JVM can’t handle all in memory. “Select entities in this viewport only”, “Select entities overlapping given polygon” etc… Datatypes, geospatial functions/operators and indexes. Support for somewhat standard SQL extensions varies a lot among databases. For some use cases, you don’t need one. Just store features as blobs, or points in lat/lon columns. Persistency Sailing Course Planner architecture
  • 11. CREATE EXTENSION postgis; CREATE TABLE geometries (name varchar, geom geometry); INSERT INTO geometries VALUES ('Point', 'POINT(0 0)'), ('Linestring', 'LINESTRING(0 0, 1 1, 2 1, 2 2)'), ('Polygon', 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'), ('PolygonWithHole', 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 … ('Collection', 'GEOMETRYCOLLECTION(POINT(2 0),POLYGON(( … SELECT name, ST_AsText(geom) FROM geometries; SELECT name, ST_AsText(geom) FROM geometries WHERE ST_Contains( ST_GeomFromText('POLYGON((-1 -1,1 -1,1 1,-1 1,-1 -1))') , geom ); name | st_astext -----------------+--------------------------------- Point | POINT(0 0) Linestring | LINESTRING(0 0,1 1,2 1,2 2) Polygon | POLYGON((0 0,1 0,1 1,0 1,0 0)) PolygonWithHole | POLYGON((0 0,10 0,10 10,0 10,0 … Collection | GEOMETRYCOLLECTION(POINT(2 0), … (5 rows) name | st_astext ---------+-------------------------------- Point | POINT(0 0) Polygon | POLYGON((0 0,1 0,1 1,0 1,0 0)) (2 rows) Example derived from the PostGIS tutorial
  • 12. JVM libraries in use Spring Boot - application framework JTS Java Topology Suite - datatypes for domain model and basic planar math Spring Data JPA/Hibernate (spatial)- object relational mapping and database connectivity Geotools - tools to handle geospatial data in Java JSR 363, Units of Measurement - conversion of distances to weird units Vaadin - the plain Java UI library for web apps Leaflet - a slippy map web component JVM Sailing Course Planner architecture
  • 13. Spring Boot An application framework, makes easy to develop and deploy web apps. Alternatives Plain servlet application, Java EE Spring Boot Sailing Course Planner architecture
  • 14. @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Route public static class MainView extends VerticalLayout { public MainView() { add(new Span("Hello World!")); } } }
  • 15. JTS aka Java Topology Suite The most commonly used library in Java apps handling geographical information. Object model (Point, LineString, Polygon…) for planar linear geometry and a set of fundamental geometric functions. C++ port (GEOM) powers PostGIS, QGIS, Google Earth etc.. Moving to Eclipse (LocationTech project) Alternatives Geolatte, Apache SIS, Spatial4j JTS Sailing Course Planner architecture
  • 16. GeometryFactory geometryFactory = new GeometryFactory(); Point point = geometryFactory.createPoint( new Coordinate(1, 1) ); LinearRing linearRing = geometryFactory.createLinearRing( new Coordinate[]{ new Coordinate(0, 0), new Coordinate(0, 2), new Coordinate(2, 2), new Coordinate(2, 0), new Coordinate(0, 0) } ); boolean contains = linearRing.contains(point); // true
  • 17. Hibernate (spatial) Hibernate 5 introduced an extension for geographic types (JTS and Geolatte) Previously a community extension Provides also some spatial functions to HQL and criteria API. Alternatives QueryDSL has a spatial extension EclipseLink (JPA implementation) contains some custom support for Oracle databases Plain JDBC (data comes in/out as WKT) JPA Sailing Course Planner architecture
  • 18. @Entity public class SpatialEvent extends AbstractEntity { private String title; @Temporal(TemporalType.DATE) private Date date; //@Column(columnDefinition = "POINT") // this type is known by MySQL @Column(columnDefinition = "geometry") private Point location; // @Column(columnDefinition = "POLYGON") // this type is known by MySQL @Column(columnDefinition = "geometry") private LineString route; //… } //Select only entities within current viewport Polygon viewPort = geometryFactory.createPolygon( coordinates ); SpatialEvent event = entityManager.createQuery( "select e " + "from SpatialEvent e " + "where within(e.location, :viewPort) = true", SpatialEvent.class) .setParameter("window", viewPort) .getSingleResult(); }
  • 19. Spring Data JPA Repository pattern for JPA No special support for geometries, but one can define SQL/JPQL/HQL explicitly. Alternatives DeltaSpike Data (CDI) JPA Sailing Course Planner architecture
  • 20. public interface SailingCourseRepository extends JpaRepository<SailingCourse,Long> { // findAll, count, save, delete, findBy(Example example) etc via super interface @EntityGraph(attributePaths = {"mainBuoys", "helperBuoys", "coursePoints", "adminEmails"}) public SailingCourse findOneWithDetailsById(Long id); @EntityGraph(attributePaths = {"mainBuoys", "helperBuoys", "coursePoints", "adminEmails"}) public SailingCourse findByUuid(String uuid); @Query("SELECT DISTINCT s FROM SailingCourse s JOIN s.adminEmails e WHERE e = :email") public List<SailingCourse> retrieveByEmail(@Param(“email") String email); }
  • 21. Geotools: tools for Java GIS apps Geometry support provided by Java Topology Suite (JTS) Read/write many raster and vector formats Tools for more complex analyses, like geodetic calculations Swing components Alternatives Spatial4j, Geolatte, Apache SIS Geotools Sailing Course Planner architecture
  • 22. double angle = 2 * Math.PI * degrees / 360; Point centroid = getCentroid(); AffineTransformation at = new AffineTransformation() .setToRotation(angle, centroid.getX(), centroid.getY()); for (MainBuoy mb : getMainBuoys()) { Geometry transformed = at.transform(mb.getLocation()); mb.setLocation((Point) transformed); } for (HelperBuoy mb : getHelperBuoys()) { Geometry transformed = at.transform(mb.getLocation()); mb.setLocation((Point) transformed); } Why Geotools? Transformation of WSG84 coordinates using JTS Don’t do this with geodesic coordinates!
  • 23. gc = new GeodeticCalculator() gc.setStartingGeographicPoint( lastKnownPoint.getLon(), lastKnownPoint.getLat() ); gc.setDestinationPosition( JTS.toDirectPosition( targetBuoy.getLocation().getCoordinate(), crs) ); int azimuth = (int) gc.getAzimuth(); if (azimuth < 0) { // convert to "compass angle" azimuth = azimuth + 360; } double orthodromicDistance = gc.getOrthodromicDistance();
  • 24. C O U N T R I E S N O T U S I N G M E T R I C S Y S T E M
  • 25. Units of Measurement API A new standard Java API for presenting and handling units of measurement. Libraries by default use SI units Not that tightly related to geospatial features, but you quite often need custom presentation for that one nation (and sailors). Alternatives Dig constants from wikipedia and build your own conversion methods JSR 363 UOM Sailing Course Planner architecture
  • 26. public static String formatDistance(double meters) { LengthAmount l = new LengthAmount(meters, SI.METRE); Quantity to = l.to(NonSI.NAUTICAL_MILE); return String.format("%d m, %.2f nm", (int) meters, to.getValue()); }
  • 27. Vaadin - Java UI framework to build modern web apps Creates XHR/WebSocket powered SPAa even with plain Java code, without web expertise. For both desktop and mobile devices. Extensible and has 700+ community extensions. Has good slippy map widgets based on Leaflet, OpenLayers and Google Maps Alternatives JSF, Wicket, React (JS), Angular (JS), Vue (JS) Vaadin Sailing Course Planner architecture
  • 28. new VerticalLayout( new Button("I'm a button"), new DatePicker("Select a date"), new ComboBox <>("Select an option"), new Span("I'm a <span>") );
  • 29. Slim OSS alternative to Google Maps Small well performing core Good support for different base maps Excellent for both desktop and mobile devices Extensible, lot of community extensions Alternatives OpenLayers, Google Maps (license!!), Mapbox GL JS (License) Leaflet Sailing Course Planner architecture
  • 30. var map = L.map(‘map’) .setView([-122.3, 37.7], 13); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); L.marker([-122.3, 37.7]).addTo(map);
  • 31. Java/Vaadin API for LeafletJS Wraps LeafletJS with Java API Trivial to integrate to other JVM technologies, no REST APIs or data conversions needed Contains Vaadin fields to directly edit JTS data types -> developer productivity Extension for editing, heat map, marker cluster… Alternatives Vaadin OpenLayers integration, JSF integrations, REST API + client side JS code (additional complexity, but access to all low level details) v-leaflet Sailing Course Planner architecture
  • 32. LMap map = new LMap(); map.addLayer(new LOpenStreetMapLayer()); map.addLayer(new LMarker(-122.3, 37.7)); private TextField title = new TextField(); private DateField date = new DateField(); private PointField location = new PointField(); private LineStringField route = new LineStringField(); public void bindEntityToForm(SpatialEvent entity) { layout.add(title, date, location, route); // naming based binding to properties in SpatialEvent BeanValidationBinder<SpatialEvent> binder = new BeanValidationBinder<>(SpatialEvent.class); binder.bindInstanceFields(this); binder.setBean(entity); }
  • 33. KEY TAKEAWAYS ADDING GEOS PATI AL FEATURES TO A JAVA WEB APP
  • 34. D E V E L O P I N G B A S I C G E O S P AT I A L F E AT U R E S I S E A S I E R T H A N YO U T H I N K
  • 35. J A V A E C O S Y S T E M H A S A L O T O F H A N D Y L I B R A R I E S F O R G I S
  • 36. V A A D I N + L E A F L E T I S P R O B A B L Y T H E E A S I E S T S O L U T I O N F O R J A V A D E V E L O P E R T O B U I L D U I
  • 37. Vaadin booth in expo area github.com/mstahv/scp vaadin.com/forum I’ll be there for more questions today & tomorrow. Source code for the Sailing Course Planner example A good place for Vaadin (+ geospatial) related questions Vaadin BOF session today 20:30 BOF5640 in room 2010, today 08:30 PM
  • 38. T H A N K S @ M A T T I T A H V O N E N
  • 39. Q U E S T I O N S ? A D D I N G G E O S P AT I A L F E AT U R E S T O A J A V A W E B A P P