Geoinformatica  0.90
Features.pm
Go to the documentation of this file.
1 package Geo::Vector::Layer::Dialogs::Features;
2 # @brief
3 
4 use strict;
5 use warnings;
6 use Carp;
7 use Gtk2::Ex::Geo::Dialogs qw/:all/;
8 use Geo::Vector::Layer::Dialogs qw/:all/;
10 
11 ## @ignore
12 # features dialog
13 sub open {
14  my($self, $gui, $soft_open) = @_;
15 
16  return if $soft_open and !$self->dialog_visible('features_dialog');
17 
18  # bootstrap:
19  my($dialog, $boot) = $self->bootstrap_dialog
20  ($gui, 'features_dialog', "Features of ".$self->name,
21  {
22  features_dialog => [delete_event => \&close_features_dialog, [$self, $gui]],
23  from_feature_spinbutton => [value_changed => \&fill_features_table, [$self, $gui]],
24  max_features_spinbutton => [value_changed => \&fill_features_table, [$self, $gui]],
25  features_limit_checkbutton => [toggled => \&fill_features_table, [$self, $gui]],
26  features_vertices_button => [clicked => \&vertices_of_selected_features, [$self, $gui]],
27  make_selection_button => [clicked => \&make_selection, [$self, $gui]],
28  print_features_button => [clicked => \&print_features, [$self, $gui]],
29  copy_selected_button => [clicked => \&copy_selected_features, [$self, $gui]],
30  zoom_to_button => [clicked => \&zoom_to_selected_features, [$self, $gui]],
31  close_features_button => [clicked => \&close_features_dialog, [$self, $gui]],
32  delete_feature_button => [clicked => \&delete_selected_features, [$self, $gui]],
33  from_drawing_button => [clicked => \&from_drawing, [$self, $gui]],
34  copy_to_drawing_button => [clicked => \&copy_to_drawing, [$self, $gui]],
35  copy_from_drawing_button => [clicked => \&copy_from_drawing, [$self, $gui]],
36  },
37  );
38 
39  if ($boot) {
40 
41  my $selection = $dialog->get_widget('feature_treeview')->get_selection;
42  $selection->set_mode('multiple');
43  $selection->signal_connect(changed => \&feature_activated, [$self, $gui]);
44 
45  }
46 
47  $dialog->get_widget('delete_feature_button')->set_sensitive($self->{update});
48  $dialog->get_widget('from_drawing_button')->set_sensitive($self->{update});
49  #$dialog->get_widget('copy_to_drawing_button')->set_sensitive($self->{update});
50  $dialog->get_widget('copy_from_drawing_button')->set_sensitive($self->{update});
51 
52  my @editable;
53  my @columns;
54  my @coltypes;
55  my @ctypes;
56  my $schema = $self->schema;
57  for my $field ($schema->fields) {
58  my $n = $field->{Name};
59  $n =~ s/_/__/g;
60  push @editable, (!($n =~ /^\./) and $self->{update}) ? 1 : 0;
61  $n =~ s/^\.//;
62  push @columns, $n;
63  push @coltypes, 'Glib::String'; # use custom sort
64  push @ctypes, $field->{Type};
65  }
66 
67  my $tv = $dialog->get_widget('feature_treeview');
68 
69  my $model = Gtk2::TreeStore->new(@coltypes);
70  $tv->set_model($model);
71 
72  for ($tv->get_columns) {
73  $tv->remove_column($_);
74  }
75 
76  my $i = 0;
77  foreach my $column (@columns) {
78  if ($ctypes[$i] eq 'Integer' or $ctypes[$i] eq 'Real') {
79  $model->set_sort_func($i, sub {
80  my($model, $a, $b, $column) = @_;
81  $a = $model->get($a, $column);
82  $a = 0 unless $a;
83  $b = $model->get($b, $column);
84  $b = 0 unless $b;
85  return $a <=> $b}, $i);
86  } else {
87  $model->set_sort_func($i, sub {
88  my($model, $a, $b, $column) = @_;
89  $a = $model->get($a, $column);
90  $a = '' unless $a;
91  $b = $model->get($b, $column);
92  $b = '' unless $b;
93  return $a cmp $b}, $i);
94  }
95  my $cell = Gtk2::CellRendererText->new;
96  $cell->set(editable => $editable[$i]);
97  $cell->signal_connect(edited => \&feature_changed, [$self, $gui, $i]);
98  my $col = Gtk2::TreeViewColumn->new_with_attributes($column, $cell, text => $i++);
99  $tv->append_column($col);
100  }
101 
102  $i = 0;
103  for ($tv->get_columns) {
104  $_->set_sort_column_id($i++);
105  $_->signal_connect(clicked => sub {
106  shift;
107  my($self, $tv) = @{$_[0]};
108  fill_features_table(undef, [$self, $gui]);
109  }, [$self, $tv]);
110  }
111 
112  fill_features_table(undef, [$self, $gui]);
113 
114  return $dialog->get_widget('features_dialog');
115 
116 }
117 
118 sub feature_changed {
119  my($cell, $path, $new_value, $data) = @_;
120  my($self, $gui, $column) = @$data;
121 
122  my $dialog = $self->{features_dialog};
123  my $treeview = $dialog->get_widget('feature_treeview');
124  my $model = $treeview->get_model;
125 
126  my $iter = $model->get_iter_from_string($path);
127  my @set = ($iter, $column, $new_value);
128  $model->set(@set);
129 
130  my @row = $model->get($iter);
131  my $fid;
132  for my $i (0..$#row) {
133  if ('FID' eq $treeview->get_column($i)->get_title) {
134  $fid = $row[$i];
135  last;
136  }
137  }
138  return unless defined $fid;
139 
140  my $f = $self->feature($fid);
141  $new_value = undef if $new_value eq '';
142  $column = $treeview->get_column($column)->get_title;
143  $column =~ s/__/_/g;
144  $f->SetField($column, $new_value);
145  $self->feature($fid, $f);
146  $self->select; # clear selection since it is a list of features read from the source
147  $gui->{overlay}->render;
148 }
149 
150 ##@ignore
151 sub close_features_dialog {
152  my($self, $gui);
153  for (@_) {
154  next unless ref eq 'ARRAY';
155  ($self, $gui) = @{$_};
156  }
157  $self->hide_dialog('features_dialog');
158  1;
159 }
160 
161 
162 ##@ignore
163 sub in_field_order {
164  my $_a = $a;
165  my $_b = $b;
166 }
167 
168 
169 ##@ignore
170 sub fill_features_table {
171  my($self, $gui) = @{$_[1]};
172 
173  my $dialog = $self->{features_dialog};
174  my $treeview = $dialog->get_widget('feature_treeview');
175  my $overlay = $gui->{overlay};
176 
177  my $from = $dialog->get_widget('from_feature_spinbutton')->get_value_as_int;
178  my $count = $dialog->get_widget('max_features_spinbutton')->get_value_as_int;
179  my $limit = $dialog->get_widget('features_limit_checkbutton')->get_active;
180 
181  $self->{_not_a_click} = 1;
182 
183  my $model = $treeview->get_model;
184  $model->clear;
185 
186  my $schema = $self->schema;
187  my @fnames = $schema->fields;
188 
189  my $features = $self->selected_features;
190 
191  my %added;
192  add_features($self, $treeview, $model, \@fnames, $features, 1, \%added);
193 
194  $count -= @$features;
195  my $is_all = 1;
196  if ($count > 0) {
197  if ($limit) {
198  my @r = $overlay->get_viewport;
199  ($features, $is_all) = $self->features( filter_rect => \@r, from => $from, limit => $count );
200  } else {
201  ($features, $is_all) = $self->features( from => $from, limit => $count );
202  }
203  add_features($self, $treeview, $model, \@fnames, $features, 0, \%added);
204  }
205  $dialog->get_widget('all_features_label')->set_sensitive($is_all);
206  delete $self->{_not_a_click};
207 }
208 
209 ##@ignore
210 sub add_features {
211  my($self, $treeview, $model, $fnames, $features, $select, $added) = @_;
212 
213  my $selection = $treeview->get_selection;
214 
215  for my $f (@$features) {
216  my @rec;
217  my $rec = 0;
218 
219  my $id = $f->GetFID;
220  next if exists $added->{$id};
221  $added->{$id} = 1;
222 
223  for my $field (@$fnames) {
224  my $name = $field->{Name};
225  if ($name =~ /^\./ or $f->IsFieldSet($name)) {
226  push @rec, $rec++;
227  my $v = Geo::Vector::feature_attribute($self, $f, $name);
228  $v = "@$v" if ref($v) eq 'ARRAY';
229  push @rec, $v;
230  } else {
231  push @rec, $rec++;
232  push @rec, undef;
233  }
234  }
235 
236  my $iter = $model->insert (undef, 999999);
237  $model->set ($iter, @rec);
238 
239  $selection->select_iter($iter) if $select;
240  }
241 }
242 
243 ## @ignore
244 sub get_hash_of_selected_features {
245  my $self = shift;
246  my %selected;
247  my $s = $self->selected_features();
248  for my $f (@$s) {
249  $selected{$f->GetFID} = $f;
250  }
251  return \%selected;
252 }
253 
254 ##@ignore
255 sub set_selected_features {
256  my($self, $treeview) = @_;
257  my $selected = $self->get_hash_of_selected_features;
258  my $selection = $treeview->get_selection;
259  my $model = $treeview->get_model;
260  my $iter = $model->get_iter_first();
261  while ($iter) {
262  my($id) = $model->get($iter, 0);
263  $selection->select_iter($iter) if $selected->{$id};
264  $iter = $model->iter_next($iter);
265  }
266 }
267 
268 ##@ignore
269 sub feature_activated {
270  my $selection = shift;
271  my($self, $gui) = @{$_[0]};
272  return if $self->{_not_a_click};
273 
274  my $features = get_selected_from_selection($selection);
275  $features = $self->features(with_id=>[keys %$features]);
276  return unless $features;
277  return unless @$features;
278  $self->selected_features($features);
279 
280  $gui->{overlay}->update_image(
281  sub {
282  my($overlay, $pixmap, $gc) = @_;
283  $gc->set_rgb_fg_color(Gtk2::Gdk::Color->new(65535,0,0));
284  for my $f (@$features) {
285  next unless $f; # should not happen
286  my $geom = $f->GetGeometryRef();
287  next unless $geom;
288  $overlay->render_geometry($gc, Geo::OGC::Geometry->new(Text => $geom->ExportToWkt));
289  }
290  }
291  );
292 }
293 
294 ##@ignore
295 sub delete_selected_features {
296  my($self, $gui) = @{$_[1]};
297  my $dialog = $self->{features_dialog};
298  my $treeview = $dialog->get_widget('feature_treeview');
299  my $features = get_selected_from_selection($treeview->get_selection);
300  $features = $self->features(with_id=>[keys %$features]);
301  $self->select;
302  for my $f (@$features) {
303  $self->{OGR}->{Layer}->DeleteFeature($f->FID);
304  }
305  fill_features_table(undef, [$self, $gui]);
306  $gui->{overlay}->render;
307 }
308 
309 ##@ignore
310 sub zoom_to_selected_features {
311  my($self, $gui) = @{$_[1]};
312 
313  my $dialog = $self->{features_dialog};
314  my $treeview = $dialog->get_widget('feature_treeview');
315  my $features = get_selected_from_selection($treeview->get_selection);
316  $features = $self->features(with_id=>[keys %$features]);
317 
318  my @viewport = $gui->{overlay}->get_viewport;
319  my @extent = ();
320 
321  for (@$features) {
322 
323  my $geom = $_->GetGeometryRef();
324  next unless $geom;
325 
326  my $env = $geom->GetEnvelope;
327  $extent[0] = $env->[0] if !defined($extent[0]) or $env->[0] < $extent[0];
328  $extent[1] = $env->[2] if !defined($extent[1]) or $env->[2] < $extent[1];
329  $extent[2] = $env->[1] if !defined($extent[2]) or $env->[1] > $extent[2];
330  $extent[3] = $env->[3] if !defined($extent[3]) or $env->[3] > $extent[3];
331 
332  }
333 
334  if (@extent) {
335 
336  # a point?
337  if ($extent[2] - $extent[0] <= 0) {
338  $extent[0] -= ($viewport[2] - $viewport[0])/10;
339  $extent[2] += ($viewport[2] - $viewport[0])/10;
340  }
341  if ($extent[3] - $extent[1] <= 0) {
342  $extent[1] -= ($viewport[3] - $viewport[1])/10;
343  $extent[3] += ($viewport[3] - $viewport[1])/10;
344  }
345 
346  $gui->{overlay}->zoom_to(@extent);
347  }
348 }
349 
350 ##@ignore
351 sub copy_selected_features {
352  my($self, $gui) = @{$_[1]};
354 }
355 
356 ##@ignore
357 sub vertices_of_selected_features {
358  my($self, $gui) = @{$_[1]};
359  # add title to the call
360  $self->open_vertices_dialog($gui);
361 }
362 
363 ##@ignore
364 sub make_selection {
365  my($self, $gui) = @{$_[1]};
366  my $dialog = $self->{features_dialog};
367  my $treeview = $dialog->get_widget('feature_treeview');
368  my $features = get_selected_from_selection($treeview->get_selection);
369  $features = $self->features(with_id=>[keys %$features]);
370  delete $gui->{overlay}->{selection};
371  for my $f (@$features) {
372  my $geom = $f->GetGeometryRef();
373  next unless $geom;
374  my $g = Geo::OGC::Geometry->new(Text => $geom->ExportToWkt);
375  unless ($gui->{overlay}->{selection}) {
376  unless ($g->isa('Geo::OGC::GeometryCollection')) {
377  my $coll = $g->MakeCollection;
378  $coll->AddGeometry($g);
379  $gui->{overlay}->{selection} = $coll;
380  } else {
381  $gui->{overlay}->{selection} = $g;
382  }
383  } else {
384  $gui->{overlay}->{selection}->AddGeometry($g);
385  }
386  }
387  $gui->{overlay}->update_image;
388 }
389 
390 ##@ignore
391 sub print_features {
392  my($self, $gui) = @{$_[1]};
393  my $dialog = $self->{features_dialog};
394  my $treeview = $dialog->get_widget('feature_treeview');
395  my $features = get_selected_from_selection($treeview->get_selection);
396  $features = $self->features(with_id=>[sort {$a<=>$b} keys %$features]);
397  my $schema = $self->schema;
398  my @fnames = map($_->{Name}, $schema->fields);
399  my @tmp = @fnames;
400  s/^\.// for @tmp;
401  print join("\t", @tmp),"\n";
402  for my $f (@$features) {
403  my @rec;
404  for my $name (@fnames) {
405  if ($name =~ /^\./ or $f->IsFieldSet($name)) {
406  my $v = Geo::Vector::feature_attribute($self, $f, $name);
407  $v = "@$v" if ref($v) eq 'ARRAY';
408  $v = 'NULL' unless defined $v;
409  push @rec, $v;
410  } else {
411  push @rec, 'NULL';
412  }
413  }
414  print join("\t", @rec),"\n";
415  }
416 }
417 
418 ##@ignore
419 sub from_drawing {
420  my($self, $gui) = @{$_[1]};
421  return unless $gui->{overlay}->{drawing};
422  $self->add_feature( Geometry => $gui->{overlay}->{drawing} );
423  fill_features_table(undef, [$self, $gui]);
424  $gui->{overlay}->render;
425 }
426 
427 ##@ignore
428 sub copy_to_drawing {
429  my($self, $gui) = @{$_[1]};
430  my $dialog = $self->{features_dialog};
431  my $treeview = $dialog->get_widget('feature_treeview');
432  my $features = get_selected_from_selection($treeview->get_selection);
433  my @features = keys %$features;
434  if (@features == 0 or @features > 1) {
435  $gui->message("Select one and only one feature.");
436  return;
437  }
438  $features = $self->features(with_id=>[@features]);
439  for my $f (@$features) {
440  my $geom = $f->GetGeometryRef();
441  next unless $geom;
442  my $g = Geo::OGC::Geometry->new(Text => $geom->ExportToWkt);
443  $gui->{overlay}->{drawing} = $g;
444  last;
445  }
446  $gui->{overlay}->update_image;
447 }
448 
449 ##@ignore
450 sub copy_from_drawing {
451  my($self, $gui) = @{$_[1]};
452  unless ($gui->{overlay}->{drawing}) {
453  $gui->message("Create a drawing first.");
454  return;
455  }
456  my $dialog = $self->{features_dialog};
457  my $treeview = $dialog->get_widget('feature_treeview');
458  my $features = get_selected_from_selection($treeview->get_selection);
459  my @features = keys %$features;
460  if (@features == 0 or @features > 1) {
461  $gui->message("Select one and only one feature.");
462  return;
463  }
464  $features = $self->features(with_id=>[@features]);
465  for my $f (@$features) {
466  my $geom = Geo::OGR::Geometry->create(WKT => $gui->{overlay}->{drawing}->AsText);
467  $f->SetGeometry($geom);
468  $self->feature($f->FID, $f);
469  last;
470  }
471  fill_features_table(undef, [$self, $gui]);
472  $gui->{overlay}->render;
473 }
474 
475 1;
public method new()
A root class for geospatial features.
Definition: Geometry.pm:69