master 64c230df49c7 cached
16 files
26.7 KB
7.0k tokens
45 symbols
1 requests
Download .txt
Repository: mstahv/spring-boot-spatial-example
Branch: master
Commit: 64c230df49c7
Files: 16
Total size: 26.7 KB

Directory structure:
gitextract_erka4_70/

├── .gitignore
├── README.md
├── pom.xml
└── src/
    ├── main/
    │   ├── java/
    │   │   └── org/
    │   │       └── vaadin/
    │   │           └── example/
    │   │               ├── AbstractEntity.java
    │   │               ├── AppShell.java
    │   │               ├── Application.java
    │   │               ├── EventEditor.java
    │   │               ├── MainView.java
    │   │               ├── SportEvent.java
    │   │               ├── SportEventRepository.java
    │   │               └── SportEventService.java
    │   └── resources/
    │       ├── application.properties
    │       └── schema-geodb.sql
    └── test/
        └── java/
            └── org/
                └── vaadin/
                    └── example/
                        ├── DatabaseTestContainerConfiguration.java
                        ├── SpatialSpringBootAppApplicationTests.java
                        └── TestApp.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
## Maven stuff
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties

# Exclude maven wrapper
!/.mvn/wrapper/maven-wrapper.jar

## Eclipse stuff
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders

# Eclipse Core
.project

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# JDT-specific (Eclipse Java Development Tools)
.classpath

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/
frontend/generated


================================================
FILE: README.md
================================================
# A Spring Boot example editing spatial data in relational database

![Alt text](./screenshot.png?raw=true "Screenshot")

## How To run?

Make sure you have Docker installed and running (needed to create test DB) and a modern Java IDE that supports at least JDK 21. (see older versions of the example if you are tied to some legacy versions).

Even if you don't want to run it, you probably want to first import the code to your favourite Java IDE (tested in IntelliJ last time) for easier exploring of the demo code. Then locate the TestApp class, and it's main method (src/main/test), run it! This will:

 * Use Docker to get a postgres with postgis extensions
 * Wire that to this Spring Boot app for development
 * Run the Vaadin UI in development mode

Alternatively, if you have Maven installed, run from CLI:

    mvn spring-boot:test-run

## What it showcases

This is a small example app that shows how one can use:

 * [Spring Boot](http://projects.spring.io/spring-boot/) and [Spring Data](https://spring.io/projects/spring-data)
 * Latest [Hibernate](http://hibernate.org/orm/) with spatial features. At the application API, only standard JPA stuff (and Spring Data) is used.
 * ~~The example also uses [QueryDSL](http://www.querydsl.com) spatial query as an example. QueryDSL contain excellent support for spatial types.~~ QueryDSL example replaced with plain JPQL(with Hibernate spatial extensions) as the latest version is not compatible with latest JTS/Hibernate. See https://github.com/querydsl/querydsl/issues/2404. If you want to see the example of QueryDSL usage in this setup, check out a bit older version of the example.
 * Relational database, like PostGis (default, Postgres + extentiosn), H2GIS or MySQL, which supports basic spatial types. The example automatically launches Docker image with PostGis for the demo using TestContainers, if run via TestApp class in src/test/java/org/vaadin/example. Not that Hibernate might need tiny adjustments for other databases.
 * [Vaadin](https://vaadin.com/) and [MapLibreGL }> add-on](https://vaadin.com/directory/component/maplibregl--add-on) to build the UI layer. MapLibre add-on is a Vaadin wrapper for [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js) slippy map widget and [mapbox-gl-draw](https://github.com/mapbox/mapbox-gl-draw). Its Vaadin field implementations which make it dead simple to edit [JTS](https://locationtech.github.io/jts/) data types directly from the JPA entities.
 * As base layer for maps, crisp vector format [OpenStreetMap](https://www.openstreetmap.org/) data via [MapTiler](https://www.maptiler.com) is used, but naturally any common background map can be used.

...to build a full-stack web app handling spatial data efficiently.

As the data is in an optimized form in the DB, it is possible to create efficient queries to the backend and e.g. only show features relevant to the current viewport of the map visualizing features or what ever you can with the spatial queries.

Enjoy!


================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.0.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.vaadin</groupId>
    <artifactId>spatial-spring-boot-app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spatial-spring-boot-app</name>
    <description>Spatial example</description>
    <properties>
        <java.version>21</java.version>
        <vaadin.version>25.0.0-rc2</vaadin.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-bom</artifactId>
                <version>${vaadin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-spatial</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-dev</artifactId>
            <!-- Only a dev time dependency -->
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.parttio</groupId>
            <artifactId>maplibre</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>in.virit</groupId>
            <artifactId>viritin</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-testcontainers</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers-junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers-postgresql</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-maven-plugin</artifactId>
                <version>${vaadin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-frontend</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>


================================================
FILE: src/main/java/org/vaadin/example/AbstractEntity.java
================================================
package org.vaadin.example;

import java.io.Serializable;
import java.util.Objects;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Version;

/**
 *
 * @author Matti Tahvonen
 */
@MappedSuperclass
public abstract class AbstractEntity implements Serializable, Cloneable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    @Version
    private int version;

    public Long getId() {
        return id;
    }

    protected void setId(Long id) {
        this.id = id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if(this.id == null) {
            return false;
        }

        if (obj instanceof AbstractEntity && obj.getClass().equals(getClass())) {
            return this.id.equals(((AbstractEntity) obj).id);
        }

        return false;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 43 * hash + Objects.hashCode(this.id);
        return hash;
    }
    
}


================================================
FILE: src/main/java/org/vaadin/example/AppShell.java
================================================
package org.vaadin.example;

import com.vaadin.flow.component.dependency.StyleSheet;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.theme.lumo.Lumo;

@StyleSheet(Lumo.STYLESHEET)
public class AppShell implements AppShellConfigurator {
}


================================================
FILE: src/main/java/org/vaadin/example/Application.java
================================================
package org.vaadin.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.vaadin.addons.maplibre.BaseMapConfigurer;

/**
 * This would be the main app for deployment artifact.
 * For deployment, you need to configure DB.
 * For local testing & development, use TestApp that
 * launches PostGIS using TestContainers.
 */
@SpringBootApplication
public class Application {

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

    // Configure default base map
    @Bean
    BaseMapConfigurer baseMapProvider() {

        return map -> {
            // NOTE, Create your own API key in maptiler! This is registered to work on localhost for the demo only
            map.initStyle("https://api.maptiler.com/maps/streets/style.json?key=G5n7stvZjomhyaVYP0qU");
        };
    }

}


================================================
FILE: src/main/java/org/vaadin/example/EventEditor.java
================================================
package org.vaadin.example;


import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import org.vaadin.addons.maplibre.LineStringField;
import org.vaadin.addons.maplibre.PointField;
import org.vaadin.firitin.components.orderedlayout.VHorizontalLayout;
import org.vaadin.firitin.components.orderedlayout.VVerticalLayout;
import org.vaadin.firitin.components.textfield.VTextField;
import org.vaadin.firitin.form.AbstractForm;

@Route
public class EventEditor extends AbstractForm<SportEvent> {

    private final SportEventService service;
    private TextField title = new VTextField("Title");
    private DatePicker date = new DatePicker("Date");
    private PointField location = new PointField("Location");
    private LineStringField route = new LineStringField("Route");

    public EventEditor(SportEventService service) {
        super(SportEvent.class);
        this.service = service;
        setSavedHandler(sportevent -> {
            service.save(sportevent);
            UI.getCurrent().navigate(MainView.class);
        });

        setResetHandler(sportevent -> {
            UI.getCurrent().navigate(MainView.class);
        });
        getDeleteButton().setVisible(false);
    }

    @Override
    protected Component createContent() {
        getContent().setSizeFull();
        location.setSizeFull();
        route.setSizeFull();
        return new VVerticalLayout()
                .withComponent(new VHorizontalLayout(title, date))
                .withExpanded(new VHorizontalLayout(location, route)
                        .withSizeFull())
                .withComponent(getToolbar())
                .withFullHeight();
    }


}


================================================
FILE: src/main/java/org/vaadin/example/MainView.java
================================================
package org.vaadin.example;

import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import in.virit.color.NamedColor;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.vaadin.addons.maplibre.DrawControl;
import org.vaadin.addons.maplibre.LinePaint;
import org.vaadin.addons.maplibre.MapLibre;
import org.vaadin.addons.maplibre.Marker;
import org.vaadin.firitin.components.RichText;
import org.vaadin.firitin.components.button.DeleteButton;
import org.vaadin.firitin.components.button.VButton;
import org.vaadin.firitin.components.grid.VGrid;
import org.vaadin.firitin.components.orderedlayout.VVerticalLayout;
import org.vaadin.firitin.components.textfield.VTextField;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author mstahv
 */
@Route
public class MainView extends VVerticalLayout {

    private final SportEventService service;
    TextField filter = new VTextField()
            .withPlaceholder("Filter by name...");

    private Map<SportEvent,Marker> eventToMarker = new HashMap<>();

    private RichText infoText = new RichText().withMarkDown(
            "###V-Leaflet example\n\n"
                    + "This is small example app to demonstrate how to add simple GIS "
                    + "features to your Spring Boot Vaadin app. "
                    + "[Check out the sources!](https://github.com/mstahv/spring-boot-spatial-example)");
    private VGrid<SportEvent> table = new VGrid<>(SportEvent.class);
    private Button addNew = new VButton("New event...")
            .withIcon(VaadinIcon.PLUS.create())
            .withClickListener(e -> {
                UI.getCurrent().navigate(EventEditor.class)
                        .get().setEntity(new SportEvent());
            });

    // Note, the base map here and in editors could be defined
    // here, but are instead defined application wide in Application class
    private MapLibre map = new MapLibre();

    public MainView(SportEventService service) {
        this.service = service;
        service.ensureTestData();
        var drawControl = new DrawControl(map);
        drawControl.addGeometryChangeListener(e -> {
            Polygon p = (Polygon) e.getGeom().getGeometryN(0);
            loadEventsWithinBounds(p);
            drawControl.clear();
        });
        add(new HorizontalLayout(
                addNew,
                new VButton("Draw area to list events", e -> {
                    drawControl.setMode(DrawControl.DrawMode.DRAW_POLYGON);
                }),
                new VButton("List events within viewport", e -> {
                    loadEventsInViewport();
                }),
                filter
        ));
        withExpanded(map, table);

        filter.addValueChangeListener(e -> {
            loadEventsByNameFilter(e.getValue());
        });

        table.addComponentColumn(sportEvent ->
                new HorizontalLayout(
                        new VButton(VaadinIcon.EDIT.create(), e-> {
                            UI.getCurrent().navigate(EventEditor.class).get()
                                    .setEntity(sportEvent);
                        }),
                        new DeleteButton(() -> {
                            delete(sportEvent);
                        })
                ));



        table.asSingleSelect().addValueChangeListener(e -> {
            SportEvent sportEvent = e.getValue();
            if(e.isFromClient() && sportEvent != null) {
                // open marker popup and center the map to event
                Marker marker = eventToMarker.get(sportEvent);
                marker.openPopup();
                map.flyTo(marker.getGeometry(), 10);
            }
        });

        loadEventsByNameFilter("");
        map.fitToContent();

    }

    public void delete(SportEvent event) {
        service.delete(event);
        loadEventsInViewport();
    }

    private void loadEventsByNameFilter(String value) {
        List<SportEvent> events = service.filterByTitle(value);
        map.fitToContent();
        setEvents(events);
    }

    private void loadEventsInViewport() {
        map.getViewPort().thenAccept(vp -> {
            Polygon bounds = vp.getBounds();
            loadEventsWithinBounds(bounds);
        });
    }

    private void loadEventsWithinBounds(Polygon bounds) {
        setEvents(service.filterByBounds(bounds));
    }

    private void setEvents(List<SportEvent> events) {
        /* Populate table... */
        table.setItems(events);
        /* ... and map */
        map.removeAll();
        eventToMarker.clear();
        for (final SportEvent sportEvent : events) {
            /*
             * Adds geometries to the map. Note that this method adds a separate
             * layer per geometry and is thus not very optimised. For better
             * performance with a large number of geometries, combine layers
             * or load features as vector tiles (~ lazy load only the visible portion)
             * if there is a ton of those.
             */

            if(sportEvent.getLocation() != null){
                Point p = sportEvent.getLocation();
                Marker marker = map.addMarker(p)
                        .withPopup(sportEvent.getTitle());
                eventToMarker.put(sportEvent,marker);
                marker.addClickListener( () -> {
                    // focus in Table
                    table.asSingleSelect().setValue(sportEvent);
                });
            }
            if(sportEvent.getRoute() != null) {
                map.addLineLayer(sportEvent.getRoute(), new LinePaint(NamedColor.BLUE, 3.0));
                // TODO add click listener also for lines
            }
        }
    }

}


================================================
FILE: src/main/java/org/vaadin/example/SportEvent.java
================================================
package org.vaadin.example;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Point;

import java.time.LocalDate;

@Entity
public class SportEvent extends AbstractEntity {

    @NotEmpty
    private String title;

    @Column(columnDefinition = "DATE")
    private LocalDate date;

    @NotNull
    @Column(columnDefinition = "geometry")
    private Point location;

    @Column(columnDefinition = "geometry")
    private LineString route;

    public SportEvent() {
    }

    public LocalDate getDate() {
        return date;
    }

    public void setDate(LocalDate date) {
        this.date = date;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public LineString getRoute() {
        return route;
    }

    public void setRoute(LineString route) {
        this.route = route;
    }

    public Point getLocation() {
        return location;
    }

    public void setLocation(Point location) {
        this.location = location;
    }

}


================================================
FILE: src/main/java/org/vaadin/example/SportEventRepository.java
================================================
package org.vaadin.example;

import java.util.List;
import org.locationtech.jts.geom.Geometry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

/**
 * @author mstahv
 */
public interface SportEventRepository extends JpaRepository<SportEvent, Long> {

    /**
     * Example method of a GIS query. This uses Hibernate spatial extensions, so
     * it does not work with other JPA implementations.
     *
     * @param bounds the geometry
     * @return SpatialEvents inside given geometry and with given filter for the title
     */
    @Query(value = "SELECT se FROM SportEvent se WHERE within(se.location, :bounds) = true")
    public List<SportEvent> findAllWithin(@Param("bounds") Geometry bounds);

    public List<SportEvent> findByTitleContainingIgnoreCase(String title);

}


================================================
FILE: src/main/java/org/vaadin/example/SportEventService.java
================================================
package org.vaadin.example;

import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

@Service
public class SportEventService {

    private final SportEventRepository repo;

    public SportEventService(SportEventRepository repository) {
        this.repo = repository;
    }

    public void ensureTestData() {
        if (repo.count() == 0) {
            GeometryFactory factory = new GeometryFactory();

            WKTReader wktReader = new WKTReader(new GeometryFactory(new PrecisionModel(), 4326));

            try {
                SportEvent theEvent = new SportEvent();
                theEvent.setTitle("Lutakon kierros");
                theEvent.setDate(LocalDate.now());
                theEvent.setLocation((Point) wktReader.read("POINT (25.77554278820253 62.2272018311204)"));
                theEvent.setRoute((LineString) wktReader.read("LINESTRING (25.77554278820253 62.2272018311204, 25.779524086129754 62.22702791012401, 25.787113435301052 62.22627424088853, 25.800301484682706 62.22586841119525, 25.802540964765228 62.2265061411197, 25.813587285546873 62.23417854401356, 25.816200012311214 62.23904679898311, 25.816448843431488 62.240843218395554, 25.80587352081554 62.24101705975687, 25.79380521148309 62.24345073358492, 25.788330926834163 62.24374044357751, 25.779497422061553 62.248201625968164, 25.77277898180992 62.251503634515785, 25.76394547703589 62.249070610653376, 25.754987556701792 62.244319855211785, 25.753743401100365 62.24240775456448, 25.749388856494107 62.23927860104487, 25.760710672470935 62.23632299124034, 25.76842443720369 62.231686157027355, 25.771270557328904 62.229011191767825, 25.77500302413449 62.227387987489095)"));
                repo.save(theEvent);

                SportEvent eventWithPath = new SportEvent();
                eventWithPath.setRoute((LineString) wktReader.read("LINESTRING (22.69504539358053 60.41742475722279, 22.697454647646992 60.41690574465318, 22.698768786227845 60.41649485382865, 22.700389557144803 60.41675436442708, 22.701046626435698 60.41671111280451, 22.702273155778926 60.415456790730445, 22.70354348974095 60.41446194917475, 22.70823058401524 60.41346707719282, 22.708537216350322 60.41281823124683, 22.710026573409664 60.41203959902907, 22.711121688894224 60.41048227868464, 22.71291767828862 60.41095813447669, 22.71414420763176 60.41193145419163, 22.715589760071282 60.411888196155985, 22.71686009403328 60.412104485759016, 22.71830564647621 60.41266683200189, 22.717604772566403 60.413164284033, 22.715940197029653 60.41283985965799, 22.713881379918092 60.412147743511554, 22.708975262547455 60.41126094817949, 22.70849341173391 60.411087912125595, 22.708186779398915 60.41242891747186, 22.708756239451105 60.412904744791774, 22.710289401129273 60.41299125810218, 22.71195397666608 60.413553589012, 22.70595274380986 60.41454845834818, 22.70595274380986 60.41521889660916, 22.70463860522895 60.41640834984031, 22.703806317460618 60.41710037534173, 22.701440868013975 60.417338255707534, 22.70043336176809 60.41720850299629, 22.69949230755762 60.417445646906515, 22.698791433647784 60.41798627584606, 22.697915341259574 60.41869989228667, 22.696075547246295 60.41872151678288, 22.694410971709488 60.418462021879435, 22.694104339373496 60.4180727756424, 22.694104339373496 60.41764027436014, 22.695155650239172 60.417424021562056)"));
                eventWithPath.setLocation((Point) wktReader.read("POINT (22.694516300414023 60.4174531804353)"));
                eventWithPath.setDate(LocalDate.now());
                eventWithPath.setTitle("MTB cup 1/10");
                repo.save(eventWithPath);

            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public void save(SportEvent sportevent) {
        repo.save(sportevent);
    }

    public void delete(SportEvent event) {
        repo.deleteById(event.getId());
    }

    public List<SportEvent> filterByTitle(String value) {
        return repo.findByTitleContainingIgnoreCase(value);
    }

    public List<SportEvent> filterByBounds(Polygon bounds) {
        return repo.findAllWithin(bounds);
    }
}


================================================
FILE: src/main/resources/application.properties
================================================
#spring.datasource.url=jdbc:mysql://localhost/spatialdemo
#spring.datasource.username=root
#spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
#spring.jpa.properties.hibernate.dialect=org.hibernate.spatial.dialect.h2geodb.GeoDBDialect
#spring.jpa.properties.hibernate.dialect=org.hibernate.spatial.dialect.mysql.MySQL56InnoDBSpatialDialect

# basic log level for all messages
logging.level.org.hibernate=info
# SQL statements and parameters
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.orm.jdbc.bind=trace
# Statistics and slow queries
logging.level.org.hibernate.stat=debug
logging.level.org.hibernate.SQL_SLOW=info
# 2nd Level Cache
logging.level.org.hibernate.cache=debug


================================================
FILE: src/main/resources/schema-geodb.sql
================================================
CREATE ALIAS InitGeoDB for "geodb.GeoDB.InitGeoDB";
CALL InitGeoDB();

================================================
FILE: src/test/java/org/vaadin/example/DatabaseTestContainerConfiguration.java
================================================
package org.vaadin.example;

import org.springframework.boot.devtools.restart.RestartScope;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;

@TestConfiguration(proxyBeanMethods = false)
class DatabaseTestContainerConfiguration {

    @Bean
    @ServiceConnection
    @RestartScope
    PostgreSQLContainer postgis() {
        PostgreSQLContainer<?> postgis = new PostgreSQLContainer<>(
                DockerImageName.parse("postgis/postgis:16-3.4-alpine").asCompatibleSubstituteFor("postgres")
        );
        return postgis;
    }
}

================================================
FILE: src/test/java/org/vaadin/example/SpatialSpringBootAppApplicationTests.java
================================================
package org.vaadin.example;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;

@Import(DatabaseTestContainerConfiguration.class)
@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads() {
    }

}


================================================
FILE: src/test/java/org/vaadin/example/TestApp.java
================================================
package org.vaadin.example;

import org.springframework.boot.SpringApplication;

public class TestApp {

    public static void main(String[] args) {
        SpringApplication.from(Application::main)
                .with(DatabaseTestContainerConfiguration.class)
                .run(args);
    }

}
Download .txt
gitextract_erka4_70/

├── .gitignore
├── README.md
├── pom.xml
└── src/
    ├── main/
    │   ├── java/
    │   │   └── org/
    │   │       └── vaadin/
    │   │           └── example/
    │   │               ├── AbstractEntity.java
    │   │               ├── AppShell.java
    │   │               ├── Application.java
    │   │               ├── EventEditor.java
    │   │               ├── MainView.java
    │   │               ├── SportEvent.java
    │   │               ├── SportEventRepository.java
    │   │               └── SportEventService.java
    │   └── resources/
    │       ├── application.properties
    │       └── schema-geodb.sql
    └── test/
        └── java/
            └── org/
                └── vaadin/
                    └── example/
                        ├── DatabaseTestContainerConfiguration.java
                        ├── SpatialSpringBootAppApplicationTests.java
                        └── TestApp.java
Download .txt
SYMBOL INDEX (45 symbols across 11 files)

FILE: src/main/java/org/vaadin/example/AbstractEntity.java
  class AbstractEntity (line 15) | @MappedSuperclass
    method getId (line 25) | public Long getId() {
    method setId (line 29) | protected void setId(Long id) {
    method equals (line 33) | @Override
    method hashCode (line 49) | @Override

FILE: src/main/java/org/vaadin/example/AppShell.java
  class AppShell (line 7) | @StyleSheet(Lumo.STYLESHEET)

FILE: src/main/java/org/vaadin/example/Application.java
  class Application (line 14) | @SpringBootApplication
    method main (line 17) | public static void main(String[] args) {
    method baseMapProvider (line 22) | @Bean

FILE: src/main/java/org/vaadin/example/EventEditor.java
  class EventEditor (line 16) | @Route
    method EventEditor (line 25) | public EventEditor(SportEventService service) {
    method createContent (line 39) | @Override

FILE: src/main/java/org/vaadin/example/MainView.java
  class MainView (line 30) | @Route
    method MainView (line 56) | public MainView(SportEventService service) {
    method delete (line 109) | public void delete(SportEvent event) {
    method loadEventsByNameFilter (line 114) | private void loadEventsByNameFilter(String value) {
    method loadEventsInViewport (line 120) | private void loadEventsInViewport() {
    method loadEventsWithinBounds (line 127) | private void loadEventsWithinBounds(Polygon bounds) {
    method setEvents (line 131) | private void setEvents(List<SportEvent> events) {

FILE: src/main/java/org/vaadin/example/SportEvent.java
  class SportEvent (line 12) | @Entity
    method SportEvent (line 28) | public SportEvent() {
    method getDate (line 31) | public LocalDate getDate() {
    method setDate (line 35) | public void setDate(LocalDate date) {
    method getTitle (line 39) | public String getTitle() {
    method setTitle (line 43) | public void setTitle(String title) {
    method getRoute (line 47) | public LineString getRoute() {
    method setRoute (line 51) | public void setRoute(LineString route) {
    method getLocation (line 55) | public Point getLocation() {
    method setLocation (line 59) | public void setLocation(Point location) {

FILE: src/main/java/org/vaadin/example/SportEventRepository.java
  type SportEventRepository (line 12) | public interface SportEventRepository extends JpaRepository<SportEvent, ...
    method findAllWithin (line 21) | @Query(value = "SELECT se FROM SportEvent se WHERE within(se.location,...
    method findByTitleContainingIgnoreCase (line 24) | public List<SportEvent> findByTitleContainingIgnoreCase(String title);

FILE: src/main/java/org/vaadin/example/SportEventService.java
  class SportEventService (line 15) | @Service
    method SportEventService (line 20) | public SportEventService(SportEventRepository repository) {
    method ensureTestData (line 24) | public void ensureTestData() {
    method save (line 52) | public void save(SportEvent sportevent) {
    method delete (line 56) | public void delete(SportEvent event) {
    method filterByTitle (line 60) | public List<SportEvent> filterByTitle(String value) {
    method filterByBounds (line 64) | public List<SportEvent> filterByBounds(Polygon bounds) {

FILE: src/test/java/org/vaadin/example/DatabaseTestContainerConfiguration.java
  class DatabaseTestContainerConfiguration (line 10) | @TestConfiguration(proxyBeanMethods = false)
    method postgis (line 13) | @Bean

FILE: src/test/java/org/vaadin/example/SpatialSpringBootAppApplicationTests.java
  class DemoApplicationTests (line 8) | @Import(DatabaseTestContainerConfiguration.class)
    method contextLoads (line 12) | @Test

FILE: src/test/java/org/vaadin/example/TestApp.java
  class TestApp (line 5) | public class TestApp {
    method main (line 7) | public static void main(String[] args) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (29K chars).
[
  {
    "path": ".gitignore",
    "chars": 944,
    "preview": "## Maven stuff\ntarget/\npom.xml.tag\npom.xml.releaseBackup\npom.xml.versionsBackup\npom.xml.next\nrelease.properties\ndependen"
  },
  {
    "path": "README.md",
    "chars": 2997,
    "preview": "# A Spring Boot example editing spatial data in relational database\n\n![Alt text](./screenshot.png?raw=true \"Screenshot\")"
  },
  {
    "path": "pom.xml",
    "chars": 4296,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "src/main/java/org/vaadin/example/AbstractEntity.java",
    "chars": 1174,
    "preview": "package org.vaadin.example;\n\nimport java.io.Serializable;\nimport java.util.Objects;\nimport jakarta.persistence.Generated"
  },
  {
    "path": "src/main/java/org/vaadin/example/AppShell.java",
    "chars": 273,
    "preview": "package org.vaadin.example;\n\nimport com.vaadin.flow.component.dependency.StyleSheet;\nimport com.vaadin.flow.component.pa"
  },
  {
    "path": "src/main/java/org/vaadin/example/Application.java",
    "chars": 968,
    "preview": "package org.vaadin.example;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconf"
  },
  {
    "path": "src/main/java/org/vaadin/example/EventEditor.java",
    "chars": 1826,
    "preview": "package org.vaadin.example;\n\n\nimport com.vaadin.flow.component.Component;\nimport com.vaadin.flow.component.UI;\nimport co"
  },
  {
    "path": "src/main/java/org/vaadin/example/MainView.java",
    "chars": 5976,
    "preview": "package org.vaadin.example;\n\nimport com.vaadin.flow.component.UI;\nimport com.vaadin.flow.component.button.Button;\nimport"
  },
  {
    "path": "src/main/java/org/vaadin/example/SportEvent.java",
    "chars": 1238,
    "preview": "package org.vaadin.example;\n\nimport jakarta.persistence.Column;\nimport jakarta.persistence.Entity;\nimport jakarta.valida"
  },
  {
    "path": "src/main/java/org/vaadin/example/SportEventRepository.java",
    "chars": 905,
    "preview": "package org.vaadin.example;\n\nimport java.util.List;\nimport org.locationtech.jts.geom.Geometry;\nimport org.springframewor"
  },
  {
    "path": "src/main/java/org/vaadin/example/SportEventService.java",
    "chars": 4484,
    "preview": "package org.vaadin.example;\n\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineStri"
  },
  {
    "path": "src/main/resources/application.properties",
    "chars": 715,
    "preview": "#spring.datasource.url=jdbc:mysql://localhost/spatialdemo\n#spring.datasource.username=root\n#spring.datasource.password=\n"
  },
  {
    "path": "src/main/resources/schema-geodb.sql",
    "chars": 69,
    "preview": "CREATE ALIAS InitGeoDB for \"geodb.GeoDB.InitGeoDB\";\nCALL InitGeoDB();"
  },
  {
    "path": "src/test/java/org/vaadin/example/DatabaseTestContainerConfiguration.java",
    "chars": 798,
    "preview": "package org.vaadin.example;\n\nimport org.springframework.boot.devtools.restart.RestartScope;\nimport org.springframework.b"
  },
  {
    "path": "src/test/java/org/vaadin/example/SpatialSpringBootAppApplicationTests.java",
    "chars": 382,
    "preview": "package org.vaadin.example;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootT"
  },
  {
    "path": "src/test/java/org/vaadin/example/TestApp.java",
    "chars": 300,
    "preview": "package org.vaadin.example;\n\nimport org.springframework.boot.SpringApplication;\n\npublic class TestApp {\n\n    public stat"
  }
]

About this extraction

This page contains the full source code of the mstahv/spring-boot-spatial-example GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (26.7 KB), approximately 7.0k tokens, and a symbol index with 45 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!