77
88import ch .rasc .sse .eventbus .SseEvent ;
99import ch .rasc .sse .eventbus .SseEventBus ;
10- import java .io .File ;
11- import java .io .IOException ;
12- import java .lang .invoke .MethodHandles ;
13- import java .nio .charset .StandardCharsets ;
14- import java .nio .file .Files ;
15- import java .nio .file .Path ;
16- import java .time .Instant ;
17- import java .util .ArrayList ;
18- import java .util .Comparator ;
19- import java .util .List ;
20- import java .util .Map ;
21- import java .util .Objects ;
22- import java .util .Set ;
23- import java .util .concurrent .TimeUnit ;
24- import java .util .concurrent .atomic .AtomicInteger ;
25- import java .util .stream .Stream ;
26- import java .util .zip .ZipEntry ;
27- import java .util .zip .ZipOutputStream ;
2810import org .apache .commons .lang3 .StringUtils ;
11+ import org .geotools .api .data .DataStore ;
2912import org .geotools .api .data .FeatureEvent ;
3013import org .geotools .api .data .FileDataStore ;
3114import org .geotools .api .data .Query ;
4326import org .geotools .data .shapefile .ShapefileDumper ;
4427import org .geotools .factory .CommonFactoryFinder ;
4528import org .geotools .feature .SchemaException ;
29+ import org .geotools .geopkg .GeoPkgDataStoreFactory ;
4630import org .geotools .util .factory .GeoTools ;
4731import org .jspecify .annotations .NonNull ;
4832import org .jspecify .annotations .Nullable ;
6650import tools .jackson .databind .SerializationFeature ;
6751import tools .jackson .databind .json .JsonMapper ;
6852
53+ import java .io .File ;
54+ import java .io .IOException ;
55+ import java .lang .invoke .MethodHandles ;
56+ import java .nio .charset .StandardCharsets ;
57+ import java .nio .file .Files ;
58+ import java .nio .file .Path ;
59+ import java .time .Instant ;
60+ import java .util .ArrayList ;
61+ import java .util .Comparator ;
62+ import java .util .List ;
63+ import java .util .Map ;
64+ import java .util .Objects ;
65+ import java .util .Set ;
66+ import java .util .concurrent .TimeUnit ;
67+ import java .util .concurrent .atomic .AtomicInteger ;
68+ import java .util .stream .Stream ;
69+ import java .util .zip .ZipEntry ;
70+ import java .util .zip .ZipOutputStream ;
71+
6972@ Service
7073public class CreateLayerExtractService {
7174 private static final Logger logger =
@@ -218,6 +221,9 @@ public void createLayerExtract(
218221 this .emitProgress (clientId , outputFileName , 0 , false , "Starting extract" );
219222
220223 switch (extractOutputFormat ) {
224+ case GEOPACKAGE ->
225+ this .handleGeoPackage (
226+ clientId , inputTmFeatureType , attributes , filter , sortBy , sortOrder , outputFileName );
221227 case SHAPE ->
222228 this .handleWithShapeDumper (
223229 clientId , inputTmFeatureType , attributes , filter , sortBy , sortOrder , outputFileName );
@@ -234,6 +240,82 @@ public void createLayerExtract(
234240 }
235241 }
236242
243+ private void handleGeoPackage (
244+ @ NonNull String clientId ,
245+ @ NonNull TMFeatureType inputTmFeatureType ,
246+ @ NonNull Set <String > attributes ,
247+ Filter filter ,
248+ String sortBy ,
249+ SortOrder sortOrder ,
250+ @ NonNull String outputFileName ) {
251+
252+ SimpleFeatureSource inputFeatureSource = null ;
253+ DataStore outputDataStore = null ;
254+
255+ try (Transaction outputTransaction = new DefaultTransaction ("tailormap-extract-output" )) {
256+ inputFeatureSource = featureSourceFactoryHelper .openGeoToolsFeatureSource (inputTmFeatureType );
257+
258+ Query q = createQuery (inputFeatureSource , attributes , filter , sortBy , sortOrder );
259+
260+ int featCount = getFeatureCount (inputFeatureSource , q );
261+ if (featCount < 0 ) {
262+ logger .warn ("Could not determine feature count for extract, progress reporting will be omitted" );
263+ }
264+
265+ outputDataStore = new GeoPkgDataStoreFactory ()
266+ .createDataStore (Map .of (
267+ GeoPkgDataStoreFactory .DBTYPE .key ,
268+ "geopkg" ,
269+ GeoPkgDataStoreFactory .DATABASE .key ,
270+ getValidatedOutputFile (outputFileName ),
271+ GeoPkgDataStoreFactory .CONTENTS_ONLY .key ,
272+ false ));
273+
274+ SimpleFeatureType fType =
275+ DataUtilities .createSubType (inputFeatureSource .getSchema (), attributes .toArray (new String [0 ]));
276+ outputDataStore .createSchema (fType );
277+
278+ final AtomicInteger featsAdded = new AtomicInteger ();
279+ if (outputDataStore .getFeatureSource (fType .getName ()) instanceof SimpleFeatureStore featureStore ) {
280+ featureStore .setTransaction (outputTransaction );
281+ featureStore .addFeatureListener (event -> {
282+ if (event .getType ().equals (FeatureEvent .Type .ADDED )) {
283+ featsAdded .getAndIncrement ();
284+ logger .debug ("Added feature {}" , featsAdded .get ());
285+ }
286+ if (featCount > 0 ) {
287+ if (featsAdded .get () % progressReportInterval == 0 ) {
288+ this .emitProgress (
289+ clientId ,
290+ outputFileName ,
291+ (int ) ((featsAdded .doubleValue () / featCount ) * 100 ),
292+ false ,
293+ null );
294+ }
295+ }
296+ });
297+ featureStore .addFeatures (inputFeatureSource .getFeatures (q ));
298+ outputTransaction .commit ();
299+ outputDataStore .dispose ();
300+ this .emitProgress (clientId , outputFileName , 100 , true , "Extract completed successfully" );
301+ }
302+ } catch (SchemaException | IOException | IllegalArgumentException e ) {
303+ emitError (clientId , e .getMessage ());
304+ logger .error ("Creating extract failed" , e );
305+ } finally {
306+ if (inputFeatureSource != null ) {
307+ try {
308+ inputFeatureSource .getDataStore ().dispose ();
309+ } catch (Exception e ) {
310+ logger .warn ("Error disposing datastore for feature source {}" , inputFeatureSource .getName (), e );
311+ }
312+ }
313+ if (outputDataStore != null ) {
314+ outputDataStore .dispose ();
315+ }
316+ }
317+ }
318+
237319 private void handleSingleFileFormats (
238320 @ NonNull String clientId ,
239321 @ NonNull TMFeatureType inputTmFeatureType ,
0 commit comments