package Geo::Vector::Layer::Dialogs::FeatureCollection;
# @brief
use strict;
use warnings;
use Carp;
use Gtk2::Ex::Geo::Dialogs qw/:all/;
use Geo::Vector::Layer::Dialogs qw/:all/;
use Geo::Vector::Layer::Dialogs::Feature;
my $DIALOG = 'feature_collection_dialog';
## @ignore
sub open {
my($self, $gui, $soft_open) = @_;
return if $soft_open and !$self->dialog_visible($DIALOG);
# bootstrap:
my($dialog, $boot) = $self->bootstrap_dialog
($gui, $DIALOG, "Features of ".$self->name,
{
$DIALOG => [delete_event => \&close_feature_collection_dialog, [$self, $gui]],
feature_collection_from_spinbutton => [value_changed => \&fill_features_table, [$self, $gui]],
feature_collection_max_spinbutton => [value_changed => \&fill_features_table, [$self, $gui]],
feature_collection_add_button => [clicked => \&add_feature, [$self, $gui]],
feature_collection_delete_feature_button => [clicked => \&delete_selected_features, [$self, $gui]],
feature_collection_from_drawing_button => [clicked => \&create_from_drawing, [$self, $gui]],
feature_collection_copy_to_drawing_button => [clicked => \©_to_drawing, [$self, $gui]],
feature_collection_copy_from_drawing_button => [clicked => \©_from_drawing, [$self, $gui]],
feature_collection_vertices_button => [clicked => \&vertices_of_selected_features, [$self, $gui]],
feature_collection_make_selection_button => [clicked => \&make_selection, [$self, $gui]],
feature_collection_copy_selected_button => [clicked => \©_selected_features, [$self, $gui]],
feature_collection_zoom_to_button => [clicked => \&zoom_to_selected_features, [$self, $gui]],
feature_collection_close_button => [clicked => \&close_feature_collection_dialog, [$self, $gui]],
}
);
if ($boot) {
my $treeview = $dialog->get_widget('feature_collection_treeview');
my $model = Gtk2::TreeStore->new('Glib::Int');
$treeview->set_model($model);
my $i = 0;
for my $column ('FID') {
my $cell = Gtk2::CellRendererText->new;
my $col = Gtk2::TreeViewColumn->new_with_attributes($column, $cell, text => $i++);
$col->set_clickable(1);
$col->signal_connect(clicked => sub {
shift;
my($self, $gui) = @{$_[0]};
fill_features_table(undef, [$self, $gui]);
}, [$self, $gui]);
$treeview->append_column($col);
}
my $selection = $treeview->get_selection;
$selection->set_mode('multiple');
$selection->signal_connect(changed => \&feature_activated, [$self, $gui]);
}
fill_features_table(undef, [$self, $gui]);
my $treeview = $dialog->get_widget('feature_collection_attributes_treeview');
my @columns = ('Field', 'Value');
my @coltypes = ('Glib::String', 'Glib::String');
my $model = Gtk2::TreeStore->new(@coltypes);
$treeview->set_model($model);
for ($treeview->get_columns) {
$treeview->remove_column($_);
}
my $i = 0;
foreach my $column (@columns) {
my $cell = Gtk2::CellRendererText->new;
#if ($column eq 'Value') {
$cell->set(editable => 1);
$cell->signal_connect(edited => \&feature_changed, [$self, $gui, $i]);
#}
my $col = Gtk2::TreeViewColumn->new_with_attributes($column, $cell, text => $i++);
$treeview->append_column($col);
}
$treeview = $dialog->get_widget('feature_collection_treeview');
feature_activated($treeview->get_selection, [$self, $gui]);
return $dialog->get_widget($DIALOG);
}
sub feature_changed {
my($cell, $path, $new_value, $data) = @_;
my($self, $gui, $column) = @$data;
my $dialog = $self->{$DIALOG};
my $features = $self->selected_features();
return unless @$features == 1;
my $feature = $features->[0];
my $treeview = $dialog->get_widget('feature_collection_attributes_treeview');
my $model = $treeview->get_model;
my $iter = $model->get_iter_from_string($path);
my($field, $value) = $model->get($iter);
return if $field eq 'FID';
return if $field eq 'Geometry type';
return if lc($field) eq 'class';
my @set = ($iter, $column, $new_value);
$model->set(@set);
if ($column == 0) {
$field = $new_value;
$value = $value;
} else {
$field = $field;
$value = $new_value;
}
return if $field eq 'add field';
#$value = undef if $value eq '';
if ($value eq 'xxx') {
$feature->DeleteField($field);
} else {
$feature->SetField($field, $value);
}
$self->feature($feature->GetFID, $feature);
$gui->{overlay}->render;
$treeview = $dialog->get_widget('feature_collection_treeview');
feature_activated($treeview->get_selection, [$self, $gui]);
}
##@ignore
sub close_feature_collection_dialog {
my($self, $gui);
for (@_) {
next unless ref eq 'ARRAY';
($self, $gui) = @{$_};
}
$self->hide_dialog($DIALOG);
1;
}
##@ignore
sub add_feature {
my($self, $gui) = @{$_[1]};
Geo::Vector::Layer::Dialogs::Feature::open($self, $gui);
}
##@ignore
sub delete_selected_features {
my($self, $gui) = @{$_[1]};
my $dialog = $self->{$DIALOG};
my $treeview = $dialog->get_widget('feature_collection_treeview');
my $delete = get_selected_from_selection($treeview->get_selection);
my @features;
my $now;
my $select;
my $feature;
for my $fid (keys %$delete) {
delete $self->{features}{$fid};
}
$self->select();
fill_features_table(undef, [$self, $gui]);
$gui->{overlay}->render;
}
##@ignore
sub create_from_drawing {
my($self, $gui) = @{$_[1]};
return unless $gui->{overlay}->{drawing};
my $feature = Geo::Vector::Feature->new();
$feature->Geometry(Geo::OGR::CreateGeometryFromWkt( $gui->{overlay}->{drawing}->AsText ));
$self->feature($feature);
$self->select(with_id => [$feature->FID]);
fill_features_table(undef, [$self, $gui]);
$gui->{overlay}->render;
}
##@ignore
sub copy_to_drawing {
my($self, $gui) = @{$_[1]};
my $dialog = $self->{$DIALOG};
my $treeview = $dialog->get_widget('feature_collection_treeview');
my $features = get_selected_from_selection($treeview->get_selection);
my @features = keys %$features;
if (@features == 0 or @features > 1) {
$gui->message("Select one and only one feature.");
return;
}
$features = $self->features(with_id=>[@features]);
for my $f (@$features) {
my $geom = $f->GetGeometryRef();
next unless $geom;
my $g = Geo::OGC::Geometry->new(Text => $geom->ExportToWkt);
$gui->{overlay}->{drawing} = $g;
last;
}
$gui->{overlay}->update_image;
}
##@ignore
sub copy_from_drawing {
my($self, $gui) = @{$_[1]};
unless ($gui->{overlay}->{drawing}) {
$gui->message("Create a drawing first.");
return;
}
my $dialog = $self->{$DIALOG};
my $treeview = $dialog->get_widget('feature_collection_treeview');
my $features = get_selected_from_selection($treeview->get_selection);
my @features = keys %$features;
if (@features == 0 or @features > 1) {
$gui->message("Select one and only one feature.");
return;
}
$features = $self->features(with_id=>[@features]);
for my $f (@$features) {
my $geom = Geo::OGR::Geometry->create(WKT => $gui->{overlay}->{drawing}->AsText);
$f->SetGeometry($geom);
$self->feature($f->FID, $f);
last;
}
$gui->{overlay}->render;
feature_activated($treeview->get_selection, [$self, $gui]);
}
##@ignore
sub vertices_of_selected_features {
my($self, $gui) = @{$_[1]};
# add title to the call
$self->open_vertices_dialog($gui);
}
##@ignore
sub make_selection {
my($self, $gui) = @{$_[1]};
my $dialog = $self->{$DIALOG};
my $treeview = $dialog->get_widget('feature_collection_treeview');
my $features = get_selected_from_selection($treeview->get_selection);
$features = $self->features(with_id=>[keys %$features]);
delete $gui->{overlay}->{selection};
for my $f (@$features) {
my $geom = $f->GetGeometryRef();
next unless $geom;
my $g = Geo::OGC::Geometry->new(Text => $geom->ExportToWkt);
unless ($gui->{overlay}->{selection}) {
unless ($g->isa('Geo::OGC::GeometryCollection')) {
my $coll = $g->MakeCollection;
$coll->AddGeometry($g);
$gui->{overlay}->{selection} = $coll;
} else {
$gui->{overlay}->{selection} = $g;
}
} else {
$gui->{overlay}->{selection}->AddGeometry($g);
}
}
$gui->{overlay}->update_image;
}
##@ignore
sub copy_selected_features {
my($self, $gui) = @{$_[1]};
Geo::Vector::Layer::Dialogs::Copy::open($self, $gui);
}
##@ignore
sub zoom_to_selected_features {
my($self, $gui) = @{$_[1]};
my $dialog = $self->{$DIALOG};
my $treeview = $dialog->get_widget('feature_collection_treeview');
my $features = get_selected_from_selection($treeview->get_selection);
$features = $self->features(with_id=>[keys %$features]);
my @viewport = $gui->{overlay}->get_viewport;
my @extent = ();
for my $f (@$features) {
my $geom = $f->Geometry();
next unless $geom;
my $env = $geom->GetEnvelope;
$extent[0] = $env->[0] if !defined($extent[0]) or $env->[0] < $extent[0];
$extent[1] = $env->[2] if !defined($extent[1]) or $env->[2] < $extent[1];
$extent[2] = $env->[1] if !defined($extent[2]) or $env->[1] > $extent[2];
$extent[3] = $env->[3] if !defined($extent[3]) or $env->[3] > $extent[3];
}
if (@extent) {
# a point?
if ($extent[2] - $extent[0] <= 0) {
$extent[0] -= ($viewport[2] - $viewport[0])/10;
$extent[2] += ($viewport[2] - $viewport[0])/10;
}
if ($extent[3] - $extent[1] <= 0) {
$extent[1] -= ($viewport[3] - $viewport[1])/10;
$extent[3] += ($viewport[3] - $viewport[1])/10;
}
$gui->{overlay}->zoom_to(@extent);
}
}
## @ignore
sub fill_features_table {
shift;
my($self, $gui) = @{$_[0]};
my $dialog = $self->{$DIALOG};
my $from = $dialog->get_widget('feature_collection_from_spinbutton')->get_value_as_int;
my $count = $dialog->get_widget('feature_collection_max_spinbutton')->get_value_as_int;
$self->{_not_a_click} = 1;
my $treeview = $dialog->get_widget('feature_collection_treeview');
my $selection = $treeview->get_selection;
my $model = $treeview->get_model;
$model->clear;
my %selected = map { $_->FID => 1 } @{$self->selected_features};
my $i = 1;
for my $f (@{$self->selected_features}) {
last if $i >= $from+$count;
$i++;
next if $i <= $from;
my $fid = $f->FID;
my $iter = $model->insert(undef, 999999);
$model->set($iter, 0, $fid);
$selection->select_iter($iter);
}
for my $fid (sort {$a <=> $b} keys %{$self->{features}}) {
next if $selected{$fid};
last if $i >= $from+$count;
$i++;
next if $i <= $from;
my $iter = $model->insert(undef, 999999);
$model->set($iter, 0, $fid);
$selection->unselect_iter($iter);
}
delete $self->{_not_a_click};
}
## @ignore
sub feature_activated {
my $selection = shift;
my($self, $gui) = @{$_[0]};
return if $self->{_not_a_click};
my $ids = get_selected_from_selection($selection);
my @ids = keys %$ids;
my $features = $self->features(with_id=>[@ids]);
$self->selected_features($features);
if ($features and @$features == 1) {
my $dialog = $self->{$DIALOG};
my $model = $dialog->get_widget('feature_collection_attributes_treeview')->get_model;
$model->clear;
my $row = $features->[0]->Row;
my $g = $row->{Geometry};
my @recs = ( # [ 0, 'FID', 1, $row->{FID} ],
[ 0, 'Geometry type', 1, $g ? $g->GeometryType : '' ],
[ 0, 'Class', 1, $row->{class} ],
);
for (sort {$a cmp $b} keys %$row) {
next if /^FID/;
next if /^Geometry/;
next if /^class/;
push @recs, [ 0, $_, 1, $row->{$_} ];
}
push @recs, [ 0, 'add field', 1, '' ];
for my $rec (@recs) {
my $iter = $model->insert (undef, 999999);
$model->set ($iter, @$rec);
}
}
$gui->{overlay}->update_image(
sub {
my($overlay, $pixmap, $gc) = @_;
$gc->set_rgb_fg_color(Gtk2::Gdk::Color->new(65535,0,0));
for my $f (@$features) {
next unless $f; # should not happen
my $geom = $f->GetGeometryRef();
next unless $geom;
$overlay->render_geometry($gc, Geo::OGC::Geometry->new(Text => $geom->ExportToWkt));
}
});
}
1;