<commit_before>=pod

=head1 NAME

Geo::OGC::Service::WFS - Perl extension for geospatial web feature services

=head1 SYNOPSIS

The process_request method of this module is called by the
Geo::OGC::Service framework.

=head1 DESCRIPTION

This module aims to provide the operations defined by the Open
Geospatial Consortium's Web Feature Service standard. These are (in
the version 2.0 of the standard)

 GetCapabilities (discovery operation) (*)
 DescribeFeatureType (discovery operation) (*)
 GetPropertyValue (query operation)
 GetFeature (query operation) (*)
 GetFeatureWithLock (query & locking operation)
 LockFeature (locking operation)
 Transaction (transaction operation) (*)
 CreateStoredQuery (stored query operation)
 DropStoredQuery (stored query operation)
 ListStoredQueries (stored query operation)
 DescribeStoredQueries (stored query operation)

(*) are at least somehow implemented.

This module is a plugin for the Geo::OGC::Service framework.

=head2 EXPORT

None by default.

=head2 METHODS

=cut

package Geo::OGC::Service::WFS;

use 5.010000; # // and //=
use feature "switch";
use Carp;
use File::Basename;
use Modern::Perl;
use Capture::Tiny ':all';
use Clone 'clone';
use JSON;
use DBI;
use Geo::GDAL;

use Data::Dumper;
use XML::LibXML::PrettyPrint;

our $VERSION = '0.06';

our %gml_geometry_type = (
    Envelope => 1,
    Point => 1,
    MultiPoint => 1,
    LineString => 1,
    MultiLineString => 1,
    Polygon => 1,
    MultiPolygon => 1,
    MultiGeometry => 1,
    );

our %spatial2op = ( 
    Disjoint => 1,
    Equals => 1,
    Touches => 1,
    Within => 1,
    Overlaps => 1,
    Crosses => 1,
    Intersects => 1,
    Contains => 1
    ); # there are more in PostGIS

our %temporal_operators = (
    After => 1,
    Before => 1,
    Begins => 1,
    BegunBy => 1,
    TContains => 1,
    During => 1,
    TEquals => 1,
    TOverlaps => 1,
    Meets => 1,
    OverlappedBy => 1,
    MetBy => 1,
    EndedBy => 1
    );

my %functions = (
    abs => ['xs:int', [[int => 'xs:int']]],
    acos => ['xs:double', [[value => 'xs:double']]],
    Area => ['xs:double', [[geometry => 'gml:AbstractGeometryType']]],
    within => ['xs:boolean', [[geometry => 'gml:AbstractGeometryType'], [geometry => 'gml:AbstractGeometryType']]]
    ); # lots missing

# GDAL and PostgreSQL data type to XML data type
our %type_map = (
    geometry => "gml:GeometryPropertyType",
    Short => "xs:short",
    Integer => "xs:integer",
    Integer64 => "xs:long",
    Real => "xs:double",
    integer => "xs:integer",
    int => "xs:integer",
    bigint => "xs:long",
    decimal => "xs:decimal",
    numeric => "xs:decimal",
    real => "xs:float",
    double => "xs:double",
    "double precision" => "xs:decimal",
    timestamp => "xs:date",
    "timestamp with time zone" => "xs:date",
    date => "xs:date",
    time => "xs:time",
    "time with time zone" => "xs:time",
    boolean => "xs:boolean",
    );

# Value in request => GDAL GML creation option
our %OutputFormats = (
    'XMLSCHEMA' => 'GML2',
    'text/xml; subtype=gml/2.1.2' => 'GML2',
    'text/xml; subtype=gml/3.1.1' => 'GML3',
    'text/xml; subtype=gml/3.2' => 'GML3.2',
    'GML3Deegree' => 'GML3Deegree',
    );

=pod

=head3 process_request

The entry method into this service. Fails unless the request is well known.

=cut

sub process_request {
    my ($self, $responder) = @_;
    $self->parse_request;
    $self->{debug} = $self->{config}{debug};
    $self->{responder} = $responder;
    if ($self->{parameters}{debug}) {
        $self->error({ 
            debug => { 
                config => $self->{config}, 
                parameters => $self->{parameters}, 
                env => $self->{env},
                request => $self->{request} 
            } });
        return;
    }
    if ($self->{debug}) {
        $self->log({ request => $self->{request}, parameters => $self->{parameters} });
        my $parser = XML::LibXML->new(no_blanks => 1);
        my $pp = XML::LibXML::PrettyPrint->new(indent_string => "  ");
        if ($self->{posted}) {
            my $dom = $parser->load_xml(string => $self->{posted});
            $pp->pretty_print($dom); # modified in-place
            say STDERR "posted:\n",$dom->toString;
        }
        if ($self->{filter}) {
            my $dom = $parser->load_xml(string => $self->{filter});
            $pp->pretty_print($dom); # modified in-place<commit_msg>Set self.debug 0 by default.<commit_after>=pod

=head1 NAME

Geo::OGC::Service::WFS - Perl extension for geospatial web feature services

=head1 SYNOPSIS

The process_request method of this module is called by the
Geo::OGC::Service framework.

=head1 DESCRIPTION

This module aims to provide the operations defined by the Open
Geospatial Consortium's Web Feature Service standard. These are (in
the version 2.0 of the standard)

 GetCapabilities (discovery operation) (*)
 DescribeFeatureType (discovery operation) (*)
 GetPropertyValue (query operation)
 GetFeature (query operation) (*)
 GetFeatureWithLock (query & locking operation)
 LockFeature (locking operation)
 Transaction (transaction operation) (*)
 CreateStoredQuery (stored query operation)
 DropStoredQuery (stored query operation)
 ListStoredQueries (stored query operation)
 DescribeStoredQueries (stored query operation)

(*) are at least somehow implemented.

This module is a plugin for the Geo::OGC::Service framework.

=head2 EXPORT

None by default.

=head2 METHODS

=cut

package Geo::OGC::Service::WFS;

use 5.010000; # say // and //=
use feature "switch";
use Carp;
use File::Basename;
use Modern::Perl;
use Capture::Tiny ':all';
use Clone 'clone';
use JSON;
use DBI;
use Geo::GDAL;

use Data::Dumper;
use XML::LibXML::PrettyPrint;

our $VERSION = '0.06';

our %gml_geometry_type = (
    Envelope => 1,
    Point => 1,
    MultiPoint => 1,
    LineString => 1,
    MultiLineString => 1,
    Polygon => 1,
    MultiPolygon => 1,
    MultiGeometry => 1,
    );

our %spatial2op = ( 
    Disjoint => 1,
    Equals => 1,
    Touches => 1,
    Within => 1,
    Overlaps => 1,
    Crosses => 1,
    Intersects => 1,
    Contains => 1
    ); # there are more in PostGIS

our %temporal_operators = (
    After => 1,
    Before => 1,
    Begins => 1,
    BegunBy => 1,
    TContains => 1,
    During => 1,
    TEquals => 1,
    TOverlaps => 1,
    Meets => 1,
    OverlappedBy => 1,
    MetBy => 1,
    EndedBy => 1
    );

my %functions = (
    abs => ['xs:int', [[int => 'xs:int']]],
    acos => ['xs:double', [[value => 'xs:double']]],
    Area => ['xs:double', [[geometry => 'gml:AbstractGeometryType']]],
    within => ['xs:boolean', [[geometry => 'gml:AbstractGeometryType'], [geometry => 'gml:AbstractGeometryType']]]
    ); # lots missing

# GDAL and PostgreSQL data type to XML data type
our %type_map = (
    geometry => "gml:GeometryPropertyType",
    Short => "xs:short",
    Integer => "xs:integer",
    Integer64 => "xs:long",
    Real => "xs:double",
    integer => "xs:integer",
    int => "xs:integer",
    bigint => "xs:long",
    decimal => "xs:decimal",
    numeric => "xs:decimal",
    real => "xs:float",
    double => "xs:double",
    "double precision" => "xs:decimal",
    timestamp => "xs:date",
    "timestamp with time zone" => "xs:date",
    date => "xs:date",
    time => "xs:time",
    "time with time zone" => "xs:time",
    boolean => "xs:boolean",
    );

# Value in request => GDAL GML creation option
our %OutputFormats = (
    'XMLSCHEMA' => 'GML2',
    'text/xml; subtype=gml/2.1.2' => 'GML2',
    'text/xml; subtype=gml/3.1.1' => 'GML3',
    'text/xml; subtype=gml/3.2' => 'GML3.2',
    'GML3Deegree' => 'GML3Deegree',
    );

=pod

=head3 process_request

The entry method into this service. Fails unless the request is well known.

=cut

sub process_request {
    my ($self, $responder) = @_;
    $self->parse_request;
    $self->{debug} = $self->{config}{debug} // 0;
    $self->{responder} = $responder;
    if ($self->{parameters}{debug}) {
        $self->error({ 
            debug => { 
                config => $self->{config}, 
                parameters => $self->{parameters}, 
                env => $self->{env},
                request => $self->{request} 
            } });
        return;
    }
    if ($self->{debug}) {
        $self->log({ request => $self->{request}, parameters => $self->{parameters} });
        my $parser = XML::LibXML->new(no_blanks => 1);
        my $pp = XML::LibXML::PrettyPrint->new(indent_string => "  ");
        if ($self->{posted}) {
            my $dom = $parser->load_xml(string => $self->{posted});
            $pp->pretty_print($dom); # modified in-place
            say STDERR "posted:\n",$dom->toString;
        }
        if ($self->{filter}) {
            my $dom = $parser->load_xml(string => $self->{filter});
            $pp->pretty_print($dom); # modified in-place
{'fraction_non_alphanumeric': 0.16364870982344953, 'fraction_numerical': 0.014938886373924853, 'mean_word_length': 2.977047704770477, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 199, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 10}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5907588', 'n_tokens_mistral': 3065, 'n_tokens_neox': 2902, 'n_words': 781}