| 1 | = query_points42.pl = |
| 2 | {{{ |
| 3 | #!perl |
| 4 | #!/usr/bin/perl |
| 5 | # This has been tested with mapserver 4.2 beta3. I found that Perl mapscript4.0.2 was not quite ready for prime |
| 6 | # time. Missing methods: class->getStyle(), colorObj support, etc. It illustrates some of the changes needed to |
| 7 | # adapt a mapscript3.6 application to mapscript4.2. Thanks to Sean Giles for all his work on mapscript. |
| 8 | ######################## |
| 9 | # This is an example of creating a layer of point objects (actually buoys in the Gulf of Maine, U.S.) It should be |
| 10 | # easily adaptable to any set of points. It creates the points as features in the layer which means that they |
| 11 | # can still be retrieved using $layer->queryByPoint() even though no shape file or database connection exists |
| 12 | # for them. |
| 13 | # In this revised edition: |
| 14 | # a) the points are drawn using $point->draw(). This allows us to do a getClass() and getStyle() for each point |
| 15 | # and set style values such as $class->{style}{size} for each point. |
| 16 | # b) We call addFeature() so that the layer is queryable, which is the main point. |
| 17 | # c) We use a numeric key value as the $shape->{index} which maps to our "database". This will be returned by |
| 18 | # queryByPoint(). |
| 19 | # d) No need to call $layer->draw() since we already called $point->draw(). Thus the features serve as |
| 20 | # an invisible layer just for querying. |
| 21 | # Author: Eric Bridger eric@gomoos.org eric@maine.com |
| 22 | # Date: |
| 23 | # This has been tested with mapserver 4.2 beta3. It does not work with mapserver4.0.2 |
| 24 | ######################## |
| 25 | |
| 26 | use strict; |
| 27 | use mapscript42; |
| 28 | use CGI ":cgi"; |
| 29 | |
| 30 | $ENV{MS_ERRORFILE} = '/tmp/mapserver4.log'; |
| 31 | |
| 32 | my $q = new CGI; |
| 33 | my $msg = ''; |
| 34 | |
| 35 | # A hash of points. First field is the key value used in addFeature() and returned by queryByPoint(). |
| 36 | # This could come from any external database, etc. |
| 37 | my %points = ( |
| 38 | 10202 => {'longitude' => -67.0173, |
| 39 | 'latitude' => 44.8911, |
| 40 | 'size' => 5, |
| 41 | 'label' => 'one', |
| 42 | }, |
| 43 | 20103 => {'longitude' => -66.0146, |
| 44 | 'latitude' => 45.2045, |
| 45 | 'size' => 10, |
| 46 | 'label' => 'two', |
| 47 | }, |
| 48 | 30105 => {'longitude' => -68.3578, |
| 49 | 'latitude' => 43.7148, |
| 50 | 'size' => 12, |
| 51 | 'label' => 'three', |
| 52 | }, |
| 53 | 40102 => {'longitude' => -66.5528, |
| 54 | 'latitude' => 43.6243, |
| 55 | 'size' => 18, |
| 56 | 'label' => 'four', |
| 57 | }, |
| 58 | 50105 => {'longitude' => -68.9983, |
| 59 | 'latitude' => 44.0555, |
| 60 | 'size' => 20, |
| 61 | 'label' => 'five', |
| 62 | }, |
| 63 | 60102 => {'longitude' => -67.8800, |
| 64 | 'latitude' => 43.4900, |
| 65 | 'size' => 10, |
| 66 | 'label' => 'six', |
| 67 | }, |
| 68 | 70104 => {'longitude' => -70.5665, |
| 69 | 'latitude' => 42.5185, |
| 70 | 'size' => 10, |
| 71 | 'label' => 'seven', |
| 72 | }, |
| 73 | 80103 => {'longitude' => -70.4278, |
| 74 | 'latitude' => 43.1807, |
| 75 | 'size' => 10, |
| 76 | 'label' => 'eight', |
| 77 | }, |
| 78 | 90104 => {'longitude' => -68.1087, |
| 79 | 'latitude' => 44.1058, |
| 80 | 'size' => 8, |
| 81 | 'label' => 'nine', |
| 82 | }, |
| 83 | 100202 => {'longitude' => -70.0578, |
| 84 | 'latitude' => 43.5673, |
| 85 | 'size' => 15, |
| 86 | 'label' => 'ten', |
| 87 | }, |
| 88 | ); |
| 89 | |
| 90 | my $image_name = sprintf("tmp/%0.10d",rand(1000000000)) . ".png"; |
| 91 | # see points42.map |
| 92 | my $map = new mapscript42::mapObj("points42.map"); |
| 93 | |
| 94 | |
| 95 | if(!$map){ |
| 96 | warn "New mapObj() error: $mapscript42::ms_error->{message}\n"; |
| 97 | } |
| 98 | |
| 99 | # Get symbol indexes from map's symbolset |
| 100 | my $circle_idx = $map->{symbolset}->index("circle"); |
| 101 | my $plus_idx = $map->{symbolset}->index("plus"); |
| 102 | |
| 103 | # Colors have changed in 4.2. No longer just an index, $clr_index = $map->addColor() is gone. |
| 104 | # You can also use $styleOjb->{color}->setRGB() and $class->{label}->{color}->setRGB(); |
| 105 | my $blue = new mapscript42::colorObj(0,0,255); |
| 106 | my $red = new mapscript42::colorObj(255,0,0); |
| 107 | my $black = new mapscript42::colorObj(0,0,0); |
| 108 | |
| 109 | # Create a point object representing the mouse click on the map. |
| 110 | my ($x, $y) = get_click($q, $map); |
| 111 | |
| 112 | my $click_pt = undef; |
| 113 | if($x != 0 && $y != 0){ |
| 114 | $click_pt = new mapscript42::pointObj(); |
| 115 | $click_pt->{x} = $x; |
| 116 | $click_pt->{y} = $y; |
| 117 | } |
| 118 | |
| 119 | # create an image for drawing. |
| 120 | my $img = $map->draw(); |
| 121 | |
| 122 | if(!$img){ |
| 123 | warn "prepareImage() error: $mapscript42::ms_error->{message}\n"; |
| 124 | } |
| 125 | |
| 126 | my $layerObj = undef; |
| 127 | |
| 128 | # Add points as Features to the point layer. |
| 129 | $layerObj = $map->getLayerByName('points'); |
| 130 | |
| 131 | |
| 132 | # Queries will return index into this array. |
| 133 | foreach my $point_id (keys %points){ |
| 134 | my $point = new mapscript42::pointObj(); |
| 135 | $point->{x} = $points{$point_id}{longitude}; |
| 136 | $point->{y} = $points{$point_id}{latitude}; |
| 137 | # Features require shape objects, which require lines, so create a single point line. |
| 138 | my $line = new mapscript42::lineObj(); |
| 139 | $line->add($point); |
| 140 | my $shp = new mapscript42::shapeObj($mapscript42::MS_SHAPE_POINT); |
| 141 | $shp->add($line); |
| 142 | #$shp->setBounds(); |
| 143 | # Don't set any text, $point->draw() will draw the text. |
| 144 | #$shp->{text} = $point_id; |
| 145 | # set the shape index to our database key value. |
| 146 | # the $shp->{index} can be any NUMERIC value. If our database key values were alphanumeric |
| 147 | # we would need to use a lookup array and set $shp-{index} to 0,1,2,... |
| 148 | # queryByPoint() results will return this value, but it must be numeric. |
| 149 | $shp->{index} = $point_id; |
| 150 | $layerObj->addFeature($shp); |
| 151 | # we only have one class in this layer. |
| 152 | my $class = $layerObj->getClass(0); |
| 153 | |
| 154 | # TWO APPROACHES illustrated here. |
| 155 | # NEW SYTLE OBJ |
| 156 | #my $style = new mapscript42::styleObj(); |
| 157 | # OR |
| 158 | # EXISTING STYLE OBJ: This picks up defaults from map file e.g. black outline color |
| 159 | my $style = $class->getStyle(0); |
| 160 | # Add new style. |
| 161 | #my $style = new mapscript42::styleObj($class); |
| 162 | |
| 163 | # point size based on our "database" |
| 164 | $style->{size} = $points{$point_id}{size}; |
| 165 | if($point_id == 40102){ |
| 166 | $style->{symbol} = $plus_idx; |
| 167 | }else{ |
| 168 | $style->{symbol} = $circle_idx; |
| 169 | } |
| 170 | #$style->{symbol} = $plus_idx; |
| 171 | |
| 172 | # COLORS |
| 173 | # just to illustrate assigning a reference to a colorObj. |
| 174 | my $color = $red; |
| 175 | if($point_id == 20103){ |
| 176 | $color = $black; |
| 177 | } |
| 178 | $style->{color} = $color; |
| 179 | |
| 180 | # since this is a STYLE from the map file we don't need this. |
| 181 | # it's in the map file. |
| 182 | $style->{outlinecolor} = $black; |
| 183 | |
| 184 | $class->{label}->{color} = $blue; |
| 185 | |
| 186 | $point->draw($map, $layerObj, $img, undef, $points{$point_id}{label}); |
| 187 | |
| 188 | # NEW STYLE must REMOVE if you inserted it. |
| 189 | #$class->removeStyle(0); |
| 190 | } |
| 191 | |
| 192 | # Query based on the mouse click point. |
| 193 | if($click_pt){ |
| 194 | $msg .= "<p>\n"; |
| 195 | # this is un-needed. |
| 196 | $layerObj = $map->getLayerByName('points'); |
| 197 | |
| 198 | if($layerObj->queryByPoint($map,$click_pt,$mapscript42::MS_SINGLE,0)){ |
| 199 | $msg .= "No Points found<br>\n"; |
| 200 | }else{ |
| 201 | # In 4.2 $layerObj-{resultcache} is no longer used. |
| 202 | # use $num_results = $layerObj->getNumResults() then |
| 203 | # foreach my $i (0 .. $num_results-1){ |
| 204 | # my $rslt = $layerObj->getResult($i); |
| 205 | #} |
| 206 | # we only expect one result. |
| 207 | my $rslt = $layerObj->getResult(0); |
| 208 | # this is the numeric value we used for the shape passed to addFeature() above. |
| 209 | my $point_id = $rslt->{shapeindex}; |
| 210 | $msg .= "Click found point: $point_id.<br>\n"; |
| 211 | $msg .= "name is: $points{$point_id}{label}.<br>\n"; |
| 212 | $msg .= "size is: $points{$point_id}{size}.<br>\n"; |
| 213 | $msg .= "lat: $points{$point_id}{latitude} long: $points{$point_id}{longitude}.<br>\n"; |
| 214 | |
| 215 | } |
| 216 | $msg .= "</p>\n"; |
| 217 | |
| 218 | } |
| 219 | |
| 220 | # display the click point |
| 221 | if($click_pt){ |
| 222 | $layerObj = $map->getLayerByName('click'); |
| 223 | $click_pt->draw($map, $layerObj, $img, undef, "Click"); |
| 224 | } |
| 225 | |
| 226 | $map->drawLabelCache($img); |
| 227 | |
| 228 | $img->save($image_name); |
| 229 | |
| 230 | # NOTA BENA Deconstructors (~) have been created for all mapscript objects. Don't call $imgObj->free(). |
| 231 | # It will be removed, but now causes core dump. |
| 232 | #$img->free(); |
| 233 | |
| 234 | # Output the HTML form and map |
| 235 | |
| 236 | print $q->header(); |
| 237 | |
| 238 | print $q->start_html(-title=>'MapServer4.2 - Dynamic Points', -bgcolor=>"#ffffff"); |
| 239 | |
| 240 | print "<form name=\"pointmap\" action=\"points42.cgi\" method=\"GET\">\n"; |
| 241 | print "<table border=\"1\" cellpadding=\"5\" cellspacing=\"2\">\n"; |
| 242 | print "<tr>\n"; |
| 243 | print "<td>\n"; |
| 244 | print "<input border=\"2\" type=\"image\" name=\"img\" src=\"$image_name\">\n"; |
| 245 | print "</td>\n"; |
| 246 | print "</tr>\n"; |
| 247 | print "</table>\n"; |
| 248 | print "</form>\n"; |
| 249 | print "$msg<br>\n"; |
| 250 | print "<p><br><br><br></p>\n"; |
| 251 | |
| 252 | |
| 253 | print $q->end_html(); |
| 254 | |
| 255 | # translate mouse click x,y into map longitude, latitude based on map extent. This is based on set_extent() in |
| 256 | # mapquakes.pl |
| 257 | |
| 258 | sub get_click { |
| 259 | my ($q, $map) = @_; |
| 260 | my ($x, $y, $cx, $cy) = (0,0,0,0); |
| 261 | my $minx = $map->{extent}->{minx}; |
| 262 | my $miny = $map->{extent}->{miny}; |
| 263 | my $maxx = $map->{extent}->{maxx}; |
| 264 | my $maxy = $map->{extent}->{maxy}; |
| 265 | |
| 266 | if($q->param('img.x')) { # Make sure we got a click |
| 267 | $x = $q->param('img.x'); |
| 268 | $y = $q->param('img.y'); |
| 269 | |
| 270 | $cx = ($maxx-$minx)/($map->{width}-1); # calculate cellsize in x and y |
| 271 | $cy = ($maxy-$miny)/($map->{height}-1); |
| 272 | |
| 273 | $x = $minx + $cx*$x; # change x,y from image to map coordinates |
| 274 | $y = $maxy - $cy*$y; |
| 275 | } |
| 276 | |
| 277 | return ($x, $y); |
| 278 | } |
| 279 | }}} |
| 280 | |
| 281 | = points42.map = |
| 282 | {{{ |
| 283 | MAP |
| 284 | NAME "points42" |
| 285 | STATUS ON |
| 286 | EXTENT -71.5 39.5 -63.0 46.0 |
| 287 | SIZE 504 385 |
| 288 | IMAGETYPE PNG |
| 289 | UNITS DD |
| 290 | PROJECTION |
| 291 | "proj=latlong" |
| 292 | END |
| 293 | OUTPUTFORMAT |
| 294 | NAME PNG |
| 295 | DRIVER "GD/PNG" |
| 296 | MIMETYPE "image/png" |
| 297 | # 24bit |
| 298 | IMAGEMODE RGB |
| 299 | # 8 bit psuedo color |
| 300 | #IMAGEMODE PC256 |
| 301 | #EXTENSION "png" |
| 302 | END |
| 303 | |
| 304 | SYMBOL |
| 305 | TYPE ELLIPSE |
| 306 | NAME "circle" |
| 307 | POINTS 1 1 END |
| 308 | FILLED TRUE |
| 309 | END |
| 310 | |
| 311 | SYMBOL |
| 312 | TYPE VECTOR |
| 313 | NAME "plus" |
| 314 | POINTS .5 0 .5 1 -99 -99 0 .5 1 .5 END |
| 315 | END |
| 316 | |
| 317 | LAYER |
| 318 | NAME "points" |
| 319 | TYPE POINT |
| 320 | STATUS ON |
| 321 | TOLERANCE 10 |
| 322 | # Need fake template for querys to work |
| 323 | TEMPLATE "bogus.html" |
| 324 | CLASS |
| 325 | NAME "buoy" |
| 326 | STYLE |
| 327 | SYMBOL "circle" |
| 328 | SIZE 0 |
| 329 | COLOR 0 255 0 |
| 330 | OUTLINECOLOR 0 0 0 |
| 331 | END |
| 332 | LABEL |
| 333 | COLOR 255 0 0 |
| 334 | TYPE BITMAP |
| 335 | SIZE MEDIUM |
| 336 | POSITION AUTO |
| 337 | PARTIALS FALSE |
| 338 | BUFFER 2 |
| 339 | END # end of label |
| 340 | END |
| 341 | PROJECTION |
| 342 | "proj=latlong" |
| 343 | END |
| 344 | END |
| 345 | |
| 346 | LAYER |
| 347 | NAME "click" |
| 348 | TYPE POINT |
| 349 | STATUS ON |
| 350 | CLASS |
| 351 | NAME "click" |
| 352 | STYLE |
| 353 | SYMBOL "plus" |
| 354 | SIZE 6 |
| 355 | COLOR 0 0 0 |
| 356 | END |
| 357 | LABEL |
| 358 | TYPE BITMAP |
| 359 | SIZE TINY |
| 360 | COLOR 0 0 0 |
| 361 | POSITION AUTO |
| 362 | PARTIALS FALSE |
| 363 | BUFFER 1 |
| 364 | END |
| 365 | END |
| 366 | END |
| 367 | |
| 368 | END |
| 369 | }}} |
| 370 | ---- |
| 371 | back to PerlMapScript |