Geo-Vector
view release on metacpan or search on metacpan
lib/Geo/Vector/Layer.pm view on Meta::CPAN
for my $field ( $schema->fields ) {
$has_int = 1, next if $field->{Type} eq 'Integer';
}
if ($has_int) {
return (
'Single color',
'Grayscale',
'Rainbow',
'Color table',
'Color bins'
);
}
else {
return ( 'Single color', 'Grayscale', 'Rainbow', 'Color bins' );
}
}
## @method @supported_symbol_types()
#
# @brief Returns a list of supported symbol types.
# @return A list (strings) of supported symbol types.
sub supported_symbol_types {
my($self) = @_;
# symbol if rendered as points or as a point (centroid of a polygon)
return ( 'No symbol', 'Square', 'Dot', 'Cross', 'Wind rose' );
my $t = $self->geometry_type;
if ( $t =~ /Point/ ) {
return ( 'Square', 'Dot', 'Cross' );
}
elsif ( $t =~ /Polygon/ ) {
return ( 'No symbol', 'Square', 'Dot', 'Cross' );
}
else {
return ();
}
}
## @ignore
sub ohoh {
for my $x (@_) {
return $x if defined $x;
}
}
## @ignore
sub has_features_with_borders {
my($self) = @_;
my $gt = $self->geometry_type;
return 1 unless $gt =~ /Point/ or $gt =~ /LineString/;
return 0;
}
## @method void render($pb)
#
# @brief Renders the vector layer onto a memory image.
#
# @param[in,out] pb Pixel buffer into which the vector layer is rendered.
# @note The layer has to be visible while using the method!
sub render {
my($self, $pb, $cr, $overlay, $viewport) = @_;
return if !$self->visible();
$self->{PALETTE_VALUE} = $PALETTE_TYPE{$self->{PALETTE_TYPE}};
$self->{SYMBOL_VALUE} = $SYMBOL_TYPE{$self->{SYMBOL_TYPE}};
if ($self->{SYMBOL_FIELD} eq 'Fixed size') {
$self->{SYMBOL_SCALE_MIN} = 0; # similar to grayscale scale
$self->{SYMBOL_SCALE_MAX} = 0;
}
my $schema = $self->schema();
$self->{COLOR_FIELD_VALUE} = ohoh(Geo::Vector::field_index($self->{COLOR_FIELD}),
$schema->field_index($self->{COLOR_FIELD}),
Geo::Vector::undefined_field_index());
$self->{SYMBOL_FIELD_VALUE} = ohoh(Geo::Vector::field_index($self->{SYMBOL_FIELD}),
$schema->field_index($self->{SYMBOL_FIELD}),
Geo::Vector::undefined_field_index());
$self->{RENDER_AS} = 'Native' unless defined $self->{RENDER_AS};
$self->{RENDER_AS_VALUE} = $Geo::Vector::RENDER_AS{ $self->{RENDER_AS} };
my @border;
if ( @{$self->{BORDER_COLOR}} and
($self->{RENDER_AS} eq 'Native' or $self->{RENDER_AS} eq 'Polygons')) {
@border = @{$self->{BORDER_COLOR}};
push @border, 255;
}
if ($self->{features}) {
for my $feature (values %{$self->{features}}) {
$self->render_feature($overlay, $cr, $feature);
}
} else {
my $handle = Geo::Vector::OGRLayerH($self->{OGR}->{Layer});
if ( not $self->{RENDERER} ) {
my $layer = Geo::Vector::ral_visual_layer_create($self, $handle);
if ($layer) {
Geo::Vector::ral_visual_layer_render( $layer, $pb ) if $pb;
Geo::Vector::ral_visual_layer_destroy($layer);
}
}
if (@border) {
my $border = Geo::Vector::Layer->new( alpha => $self->{ALPHA}, single_color => \@border );
$border->{RENDER_AS_VALUE} = $Geo::Vector::RENDER_AS{Lines};
my $layer = Geo::Vector::ral_visual_layer_create($border, $handle);
if ($layer) {
Geo::Vector::ral_visual_layer_render( $layer, $pb ) if $pb;
Geo::Vector::ral_visual_layer_destroy($layer);
}
}
$self->render_labels($cr, $overlay, $viewport);
}
}
sub render_labels {
my($self, $cr, $overlay, $viewport) = @_;
my $labeling = $self->labeling;
return unless $labeling->{field} ne 'No Labels';
my @label_color = @{$labeling->{color}};
$label_color[3] = int($self->{ALPHA}*$label_color[3]/255);
for (@label_color) {
$_ /= 255;
}
my $wc = -0.5;
my $hc = -0.5;
my $dw = 0;
for ($labeling->{placement}) {
$hc = -1 - $self->{LABEL_VERT_NUDGE} if /Top/;
$hc = $self->{LABEL_VERT_NUDGE} if /Bottom/;
if (/left/) {$wc = -1; $dw = -1*$self->{LABEL_HORIZ_NUDGE_LEFT}};
if (/right/) {$wc = 0; $dw = $self->{LABEL_HORIZ_NUDGE_RIGHT}};
}
my $font_desc = Gtk2::Pango::FontDescription->from_string($labeling->{font});
$self->{OGR}->{Layer}->SetSpatialFilterRect(@$viewport);
$self->{OGR}->{Layer}->ResetReading();
my %geohash;
my $f;
# later this should be as in libral, color may be a function
my @color = @{$self->{SINGLE_COLOR}};
$label_color[3] = int($self->{ALPHA}*$color[3]/255);
for (@color) {
$_ /= 255;
}
while ($f = $self->{OGR}->{Layer}->GetNextFeature()) {
my $geometry = $f->GetGeometryRef();
my @placements = label_placement($geometry, $overlay->{pixel_size}, @$viewport, $f->GetFID);
for (@placements) {
my ($size, @point) = @$_;
last unless (@point and defined($point[0]) and defined($point[1]));
next if ($labeling->{min_size} > 0 and $size < $labeling->{min_size});
next if
$point[0] < $viewport->[0] or
$point[0] > $viewport->[2] or
$point[1] < $viewport->[1] or
$point[1] > $viewport->[3];
my @pixel = $overlay->point2pixmap_pixel(@point);
if ($self->{INCREMENTAL_LABELS}) {
# this is fast but not very good
my $geokey = int($pixel[0]/120) .'-'. int($pixel[1]/50);
next if $geohash{$geokey};
$geohash{$geokey} = 1;
}
if ($self->{RENDERER} eq 'Cairo') {
my $points = $geometry->Points;
# now only for points
my @p = $overlay->point2pixmap_pixel(@{$points->[0]});
my $d = $self->{SYMBOL_SIZE}/2;
$cr->move_to($p[0]-$d, $p[1]);
$cr->line_to($p[0]+$d, $p[1]);
$cr->move_to($p[0], $p[1]-$d);
$cr->line_to($p[0], $p[1]+$d);
$cr->set_line_width($self->{LINE_WIDTH});
$cr->set_source_rgba(@color);
$cr->stroke();
}
my $str = Geo::Vector::feature_attribute($self, $f, $labeling->{field});
next unless defined $str or $str eq '';
my $layout = Gtk2::Pango::Cairo::create_layout($cr);
$layout->set_font_description($font_desc);
$layout->set_text($str);
my($width, $height) = $layout->get_pixel_size;
$cr->move_to($pixel[0]+$wc*$width+$dw, $pixel[1]+$hc*$height);
$cr->set_source_rgba(@label_color);
Gtk2::Pango::Cairo::show_layout($cr, $layout);
}
}
}
sub render_feature {
my($self, $overlay, $cr, $feature, $geometry) = @_;
$geometry = $feature->Geometry unless $geometry;
my $t = $geometry->GeometryType;
my $a = $self->alpha/255.0;
my @color = $self->single_color;
for (@color) {
$_ /= 255.0;
$_ *= $a;
}
if ($t =~ /^Point/) {
render_point($overlay, $cr, $geometry, \@color);
} elsif ($t =~ /^Line/) {
render_linestring($overlay, $cr, $geometry, 1, \@color);
} elsif ($t =~ /^Poly/) {
my @border = $self->border_color;
@border = (0,0,0) unless @border;
for (@border) {
$_ /= 255.0;
$_ *= $a;
}
push @border, $color[3];
render_polygon($overlay, $cr, $geometry, 1, \@border, \@color);
} elsif ($geometry->GetGeometryCount > 0) {
for my $i (0..$geometry->GetGeometryCount-1) {
render_feature($self, $overlay, $cr, $feature, $geometry->GetGeometryRef($i));
}
}
}
sub render_polygon {
my($overlay, $cr, $geometry, $line_width, $border, $fill) = @_;
paths($overlay, $cr, $geometry->Points);
$cr->set_line_width($line_width);
$cr->set_source_rgba(@$fill);
$cr->set_fill_rule('even-odd');
$cr->fill_preserve;
$cr->set_source_rgba(@$border);
$cr->stroke;
}
sub render_linestring {
my($overlay, $cr, $geometry, $line_width, $color) = @_;
$cr->set_line_width($line_width);
$cr->set_source_rgba(@$color);
geometry_path($overlay, $cr, $geometry);
$cr->stroke;
}
sub render_point {
my($overlay, $cr, $geometry, $color) = @_;
$cr->set_line_width(1);
$cr->set_source_rgba(@$color);
my @p = $overlay->point2surface($geometry->GetPoint);
for (@p) {
$_ = bounds($_, -10000, 10000);
}
$p[0] -= 3;
$cr->move_to(@p);
$p[0] += 6;
$cr->line_to(@p);
$p[0] -= 3;
$p[1] -= 3;
$cr->move_to(@p);
$p[1] += 6;
$cr->line_to(@p);
$cr->stroke;
}
sub geometry_path {
my($overlay, $cr, $geometry) = @_;
if ($geometry->GetGeometryCount > 0) {
for my $i (0..$geometry->GetGeometryCount-1) {
geometry_path($overlay, $cr, $geometry->GetGeometryRef($i));
}
} else {
path($overlay, $cr, $geometry->Points);
}
}
sub paths {
my($overlay, $cr, $points) = @_;
if (ref $points->[0]->[0]) {
for my $i (0..$#$points) {
paths($overlay, $cr, $points->[$i]);
}
} else {
path($overlay, $cr, $points);
}
}
sub path {
my($overlay, $cr, $points) = @_;
my @p = $overlay->point2surface(@{$points->[0]});
for (@p) {
$_ = bounds($_, -10000, 10000);
}
$cr->move_to(@p);
for my $i (1..$#$points) {
@p = $overlay->point2surface(@{$points->[$i]});
for (@p) {
$_ = bounds($_, -10000, 10000);
}
$cr->line_to(@p);
}
}
sub bounds {
$_[0] < $_[1] ? $_[1] : ($_[0] > $_[2] ? $_[2] : $_[0]);
}
##@ignore
sub piece_of_line_string {
my($geom, $i0, $minx, $miny, $maxx, $maxy) = @_;
my($x, $y);
while(1) {
$x = $geom->GetX($i0);
$y = $geom->GetY($i0);
last if $x >= $minx and $y >= $miny and $x <= $maxx and $y <= $maxy;
$i0++;
return if $i0 >= $geom->GetPointCount-1;
}
my $l = 0;
my $i1 = $i0+1;
my $x0 = $x;
my $y0 = $y;
while (1) {
$x = $geom->GetX($i1);
$y = $geom->GetY($i1);
$l += sqrt(($x0-$x)*($x0-$x)+($y0-$y)*($y0-$y));
last if $x < $minx or $y < $miny or $x > $maxx or $y > $maxy;
$i1++;
last if $i1 >= $geom->GetPointCount;
$x0 = $x;
$y0 = $y;
}
return ($i0, $i1, $l);
}
##@ignore
sub label_placement {
my($geom, $scale, $minx, $miny, $maxx, $maxy, $fid) = @_;
my $type = $geom->GetGeometryType & ~0x80000000;
if ($type == $Geo::OGR::wkbPoint) {
return ([0, $geom->GetX(0), $geom->GetY(0)]);
}
elsif ($type == $Geo::OGR::wkbLineString) {
my $i0 = 0;
my $i1;
my $len;
my @placements;
while (1) {
($i0, $i1, $len) = piece_of_line_string($geom, $i0, $minx, $miny, $maxx, $maxy);
last unless defined $i0;
# a label between i0 and i1
my $h = $len/2;
my $x0 = $geom->GetX($i0);
my $y0 = $geom->GetY($i0);
lib/Geo/Vector/Layer.pm view on Meta::CPAN
last if $i1 >= $geom->GetPointCount;
$i0 = $i1;
}
return @placements;
}
elsif ($type == $Geo::OGR::wkbPolygon) {
my $c = $geom->Centroid;
return ([$geom->GetArea/($scale*$scale), $c->GetX, $c->GetY]);
}
elsif ($type == $Geo::OGR::wkbMultiLineString or $type == $Geo::OGR::wkbMultiLineString25D) {
my $len = 0;
my $longest = -1;
for my $i (0..$geom->GetGeometryCount()-1) {
my $a = line_string_length($geom->GetGeometryRef($i));
if ($a > $len) {
$len = $a;
$longest = $i;
}
}
return label_placement($geom->GetGeometryRef($longest), $scale) if $longest >= 0;
}
elsif ($type == $Geo::OGR::wkbMultiPolygon or $type == $Geo::OGR::wkbGeometryCollection) {
my $size = 0;
my $largest = -1;
for my $i (0..$geom->GetGeometryCount()-1) {
my $a = $geom->GetGeometryRef($i)->GetArea;
if ($a > $size) {
$size = $a;
$largest = $i;
}
}
return label_placement($geom->GetGeometryRef($largest), $scale) if $largest >= 0;
} else {
my $t = Geo::OGR::Geometry::TYPE_INT2STRING{$type};
print STDERR "label placement not defined for geometry type $t\n";
return ();
}
print STDERR "couldn't compute label placement\n";
return ();
}
##@ignore
sub line_string_length {
my $line = shift;
my $l = 0;
my $x0 = $line->GetX(0);
my $y0 = $line->GetY(0);
for (1..$line->GetPointCount-1) {
my $x1 = $line->GetX($_);
my $y1 = $line->GetY($_);
$l += sqrt(($x1-$x0)*($x1-$x0)+($y1-$y0)*($y1-$y0));
$x0 = $x1;
$y0 = $y1;
}
return $l;
}
## @ignore
sub render_selection {
my($self, $gc, $overlay) = @_;
my $features = $self->selected_features();
for my $f (@$features) {
next unless $f; # should not happen
my $geom = $f->GetGeometryRef();
next unless $geom;
# this could be a bit faster without conversion
$overlay->render_geometry($gc, Geo::OGC::Geometry->new(Text => $geom->ExportToWkt));
}
}
# this piece should probably go into GDAL:
package Geo::OGR::Driver;
use vars qw /%FormatNames/;
%FormatNames = (
'AVCBin' => 'Arc/Info Binary Coverage',
'AVCE00' => 'Arc/Info .E00 (ASCII) Coverage',
'BNA' => 'Atlas BNA',
'DXF' => 'AutoCAD DXF',
'CSV' => 'Comma Separated Value (.csv)',
'DODS' => 'DODS/OPeNDAP',
'PGeo' => 'ESRI Personal GeoDatabase',
'SDE' => 'ESRI ArcSDE',
'ESRI Shapefile' => 'ESRI Shapefile',
'FMEObjects Gateway' => 'FMEObjects Gateway',
'GeoJSON' => 'GeoJSON',
'Geoconcept' => 'Geoconcept Export',
'GeoRSS' => 'GeoRSS',
'GML' => 'GML',
'GMT' => 'GMT',
'GPX' => 'GPX',
'GRASS' => 'GRASS',
'GPSTrackMaker' => 'GPSTrackMaker (.gtm, .gtz)',
'IDB' => 'Informix DataBlade',
'Interlis 1' => 'INTERLIS',
'Interlis 2' => 'INTERLIS',
'INGRES' => 'INGRES',
'KML' => 'KML',
'MapInfo File' => 'Mapinfo File',
'DGN' => 'Microstation DGN',
'Memory' => 'Memory',
'MySQL' => 'MySQL',
'OCI' => 'Oracle Spatial',
'ODBC' => 'ODBC',
'OGDI' => 'OGDI Vectors',
'PCIDSK' => 'PCI Geomatics Database File',
'PostgreSQL' => 'PostgreSQL',
'REC' => 'EPIInfo .REC',
'S57' => 'S-57 (ENC)',
'SDTS' => 'SDTS',
'SQLite' => 'SQLite/SpatiaLite',
'UK. NTF' => 'UK .NTF',
'TIGER' => 'U.S. Census TIGER/Line',
'VFK' => 'VFK data',
'VRT' => 'VRT - Virtual Datasource',
'XPLANE' => 'X-Plane/Flightgear aeronautical data',
);
## @ignore
sub DataSourceTemplate {
my($self) = @_;
my $n = $self->GetName;
# return simplified BNF and tell more in help string
if ($n eq 'DODS') {
return ('DODS:<URL>','');
( run in 1.122 second using v1.01-cache-2.11-cpan-39bf76dae61 )