001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/displayelements/LabelFactory.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General public static License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstr. 19 030 53115 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.graphics.displayelements; 044 045 import java.awt.Color; 046 import java.awt.Font; 047 import java.awt.Graphics2D; 048 import java.awt.font.FontRenderContext; 049 import java.awt.font.LineMetrics; 050 import java.awt.geom.Rectangle2D; 051 import java.util.ArrayList; 052 import java.util.Collections; 053 import java.util.Iterator; 054 import java.util.List; 055 056 import org.deegree.graphics.sld.Halo; 057 import org.deegree.graphics.sld.LabelPlacement; 058 import org.deegree.graphics.sld.LinePlacement; 059 import org.deegree.graphics.sld.PointPlacement; 060 import org.deegree.graphics.sld.TextSymbolizer; 061 import org.deegree.graphics.transformation.GeoTransform; 062 import org.deegree.model.feature.Feature; 063 import org.deegree.model.filterencoding.FilterEvaluationException; 064 import org.deegree.model.spatialschema.Curve; 065 import org.deegree.model.spatialschema.Geometry; 066 import org.deegree.model.spatialschema.GeometryException; 067 import org.deegree.model.spatialschema.GeometryFactory; 068 import org.deegree.model.spatialschema.LineString; 069 import org.deegree.model.spatialschema.MultiCurve; 070 import org.deegree.model.spatialschema.MultiPoint; 071 import org.deegree.model.spatialschema.MultiSurface; 072 import org.deegree.model.spatialschema.Point; 073 import org.deegree.model.spatialschema.Position; 074 import org.deegree.model.spatialschema.Surface; 075 076 /** 077 * Does the labeling, i.e. creates (screen) <tt>Label</tt> representations from 078 * <tt>LabelDisplayElement</tt>s. 079 * <p> 080 * Different geometry-types (of the LabelDisplayElement) imply different strategies concerning the 081 * way the <tt>Labels</tt> are generated. 082 * <p> 083 * 084 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a> 085 * @version $Revision: 9340 $ $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $ 086 */ 087 public class LabelFactory { 088 089 /** 090 * @param caption 091 * @param font 092 * @param color 093 * @param metrics 094 * @param feature 095 * @param halo 096 * @param x 097 * @param y 098 * @param w 099 * @param h 100 * @param rotation 101 * @param anchorPointX 102 * @param anchorPointY 103 * @param displacementX 104 * @param displacementY 105 * @return label-representations 106 */ 107 public static Label createLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, 108 Halo halo, int x, int y, int w, int h, double rotation, double anchorPointX, 109 double anchorPointY, double displacementX, double displacementY ) { 110 if ( rotation == 0.0 ) { 111 return new HorizontalLabel( caption, font, color, metrics, feature, halo, x, y, w, h, 112 new double[] { anchorPointX, anchorPointY }, new double[] { displacementX, 113 displacementY } ); 114 } 115 return new RotatedLabel( caption, font, color, metrics, feature, halo, x, y, w, h, rotation, 116 new double[] { anchorPointX, anchorPointY }, new double[] { displacementX, 117 displacementY } ); 118 119 } 120 121 /** 122 * @param caption 123 * @param font 124 * @param color 125 * @param metrics 126 * @param feature 127 * @param halo 128 * @param x 129 * @param y 130 * @param w 131 * @param h 132 * @param rotation 133 * @param anchorPoint 134 * @param displacement 135 * @return label-representations 136 */ 137 public static Label createLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, 138 Halo halo, int x, int y, int w, int h, double rotation, double[] anchorPoint, 139 double[] displacement ) { 140 if ( rotation == 0.0 ) { 141 return new HorizontalLabel( caption, font, color, metrics, feature, halo, x, y, w, h, anchorPoint, 142 displacement ); 143 } 144 return new RotatedLabel( caption, font, color, metrics, feature, halo, x, y, w, h, rotation, anchorPoint, 145 displacement ); 146 147 } 148 149 /** 150 * Generates label-representations for a given <tt>LabelDisplayElement</tt>. 151 * <p> 152 * 153 * @param element 154 * @param projection 155 * @param g 156 * @return label-representations 157 * @throws Exception 158 */ 159 public static Label[] createLabels( LabelDisplayElement element, GeoTransform projection, Graphics2D g ) 160 throws Exception { 161 162 Label[] labels = new Label[0]; 163 Feature feature = element.getFeature(); 164 String caption = element.getLabel().evaluate( feature ); 165 166 // sanity check: empty labels are ignored 167 if ( caption == null || caption.trim().equals( "" ) ) { 168 return labels; 169 } 170 171 Geometry geometry = element.getGeometry(); 172 TextSymbolizer symbolizer = (TextSymbolizer) element.getSymbolizer(); 173 174 // gather font information 175 org.deegree.graphics.sld.Font sldFont = symbolizer.getFont(); 176 java.awt.Font font = new java.awt.Font( sldFont.getFamily( feature ), sldFont.getStyle( feature ) 177 | sldFont.getWeight( feature ), 178 sldFont.getSize( feature ) ); 179 g.setFont( font ); 180 FontRenderContext frc = g.getFontRenderContext(); 181 Rectangle2D bounds = font.getStringBounds( caption, frc ); 182 LineMetrics metrics = font.getLineMetrics( caption, frc ); 183 int w = (int) bounds.getWidth(); 184 int h = (int) bounds.getHeight(); 185 // int descent = (int) metrics.getDescent (); 186 187 if ( geometry instanceof Point || geometry instanceof MultiPoint ) { 188 189 // get screen coordinates 190 int[] coords = calcScreenCoordinates( projection, geometry ); 191 int x = coords[0]; 192 int y = coords[1]; 193 194 // default placement information 195 double rotation = 0.0; 196 double[] anchorPoint = { 0.0, 0.5 }; 197 double[] displacement = { 0.0, 0.0 }; 198 199 // use placement information from SLD 200 LabelPlacement lPlacement = symbolizer.getLabelPlacement(); 201 202 if ( lPlacement != null ) { 203 PointPlacement pPlacement = lPlacement.getPointPlacement(); 204 anchorPoint = pPlacement.getAnchorPoint( feature ); 205 displacement = pPlacement.getDisplacement( feature ); 206 rotation = pPlacement.getRotation( feature ); 207 } 208 209 labels = new Label[] { createLabel( caption, font, sldFont.getColor( feature ), metrics, feature, 210 symbolizer.getHalo(), x, y, w, h, rotation, anchorPoint, displacement ) }; 211 } else if ( geometry instanceof Surface || geometry instanceof MultiSurface ) { 212 213 // get screen coordinates 214 int[] coords = calcScreenCoordinates( projection, geometry ); 215 int x = coords[0]; 216 int y = coords[1]; 217 218 // default placement information 219 double rotation = 0.0; 220 double[] anchorPoint = { 0.5, 0.5 }; 221 double[] displacement = { 0.0, 0.0 }; 222 223 // use placement information from SLD 224 LabelPlacement lPlacement = symbolizer.getLabelPlacement(); 225 226 if ( lPlacement != null ) { 227 PointPlacement pPlacement = lPlacement.getPointPlacement(); 228 229 // check if the label is to be centered within the intersection 230 // of 231 // the screen surface and the polygon geometry 232 if ( pPlacement.isAuto() ) { 233 Surface screenSurface = GeometryFactory.createSurface( projection.getSourceRect(), null ); 234 Geometry intersection = screenSurface.intersection( geometry ); 235 if ( intersection != null ) { 236 Position source = intersection.getCentroid().getPosition(); 237 x = (int) ( projection.getDestX( source.getX() ) + 0.5 ); 238 y = (int) ( projection.getDestY( source.getY() ) + 0.5 ); 239 } 240 } 241 anchorPoint = pPlacement.getAnchorPoint( feature ); 242 displacement = pPlacement.getDisplacement( feature ); 243 rotation = pPlacement.getRotation( feature ); 244 } 245 246 labels = new Label[] { createLabel( caption, font, sldFont.getColor( feature ), metrics, feature, 247 symbolizer.getHalo(), x, y, w, h, rotation, anchorPoint, displacement ) 248 249 }; 250 } else if ( geometry instanceof Curve || geometry instanceof MultiCurve ) { 251 Surface screenSurface = GeometryFactory.createSurface( projection.getSourceRect(), null ); 252 Geometry intersection = screenSurface.intersection( geometry ); 253 if ( intersection != null ) { 254 List list = null; 255 if ( intersection instanceof Curve ) { 256 list = createLabels( (Curve) intersection, element, g, projection ); 257 } else if ( intersection instanceof MultiCurve ) { 258 list = createLabels( (MultiCurve) intersection, element, g, projection ); 259 } else { 260 throw new Exception( "Intersection produced unexpected " + "geometry type: '" 261 + intersection.getClass().getName() + "'!" ); 262 } 263 labels = new Label[list.size()]; 264 for ( int i = 0; i < labels.length; i++ ) { 265 Label label = (Label) list.get( i ); 266 labels[i] = label; 267 } 268 } 269 } else { 270 throw new Exception( "LabelFactory does not implement generation " + "of Labels from geometries of type: '" 271 + geometry.getClass().getName() + "'!" ); 272 } 273 return labels; 274 } 275 276 /** 277 * Determines positions on the given <tt>MultiCurve</tt> where a caption could be drawn. For 278 * each of this positons, three candidates are produced; one on the line, one above of it and 279 * one below. 280 * <p> 281 * 282 * @param multiCurve 283 * @param element 284 * @param g 285 * @param projection 286 * @return ArrayList containing Arrays of Label-objects 287 * @throws FilterEvaluationException 288 */ 289 public static List createLabels( MultiCurve multiCurve, LabelDisplayElement element, Graphics2D g, 290 GeoTransform projection ) 291 throws FilterEvaluationException { 292 293 List<Label> placements = Collections.synchronizedList( new ArrayList<Label>( 10 ) ); 294 for ( int i = 0; i < multiCurve.getSize(); i++ ) { 295 Curve curve = multiCurve.getCurveAt( i ); 296 placements.addAll( createLabels( curve, element, g, projection ) ); 297 } 298 return placements; 299 } 300 301 /** 302 * Determines positions on the given <tt>Curve</tt> where a caption could be drawn. For each 303 * of this positons, three candidates are produced; one on the line, one above of it and one 304 * below. 305 * <p> 306 * 307 * @param curve 308 * @param element 309 * @param g 310 * @param projection 311 * @return ArrayList containing Arrays of Label-objects 312 * @throws FilterEvaluationException 313 */ 314 public static ArrayList<Label> createLabels( Curve curve, LabelDisplayElement element, Graphics2D g, 315 GeoTransform projection ) 316 throws FilterEvaluationException { 317 318 Feature feature = element.getFeature(); 319 320 // determine the placement type and parameters from the TextSymbolizer 321 double perpendicularOffset = 0.0; 322 int placementType = LinePlacement.TYPE_ABSOLUTE; 323 double lineWidth = 3.0; 324 int gap = 6; 325 TextSymbolizer symbolizer = ( (TextSymbolizer) element.getSymbolizer() ); 326 if ( symbolizer.getLabelPlacement() != null ) { 327 LinePlacement linePlacement = symbolizer.getLabelPlacement().getLinePlacement(); 328 if ( linePlacement != null ) { 329 placementType = linePlacement.getPlacementType( element.getFeature() ); 330 perpendicularOffset = linePlacement.getPerpendicularOffset( element.getFeature() ); 331 lineWidth = linePlacement.getLineWidth( element.getFeature() ); 332 gap = linePlacement.getGap( element.getFeature() ); 333 } 334 } 335 336 // get width & height of the caption 337 String caption = element.getLabel().evaluate( element.getFeature() ); 338 org.deegree.graphics.sld.Font sldFont = symbolizer.getFont(); 339 java.awt.Font font = new java.awt.Font( sldFont.getFamily( element.getFeature() ), 340 sldFont.getStyle( element.getFeature() ) 341 | sldFont.getWeight( element.getFeature() ), 342 sldFont.getSize( element.getFeature() ) ); 343 g.setFont( font ); 344 FontRenderContext frc = g.getFontRenderContext(); 345 Rectangle2D bounds = font.getStringBounds( caption, frc ); 346 LineMetrics metrics = font.getLineMetrics( caption, frc ); 347 double width = bounds.getWidth(); 348 double height = bounds.getHeight(); 349 350 // get screen coordinates of the line 351 int[][] pos = calcScreenCoordinates( projection, curve ); 352 353 // ideal distance from the line 354 double delta = height / 2.0 + lineWidth / 2.0; 355 356 // walk along the linestring and "collect" possible placement positions 357 int w = (int) width; 358 int lastX = pos[0][0]; 359 int lastY = pos[1][0]; 360 int count = pos[2][0]; 361 int boxStartX = lastX; 362 int boxStartY = lastY; 363 364 ArrayList<Label> labels = new ArrayList<Label>( 100 ); 365 List<int[]> eCandidates = Collections.synchronizedList( new ArrayList<int[]>( 100 ) ); 366 int i = 0; 367 int kk = 0; 368 while ( i < count && kk < 100 ) { 369 kk++; 370 int x = pos[0][i]; 371 int y = pos[1][i]; 372 373 // segment found where endpoint of box should be located? 374 if ( getDistance( boxStartX, boxStartY, x, y ) >= w ) { 375 376 int[] p0 = new int[] { boxStartX, boxStartY }; 377 int[] p1 = new int[] { lastX, lastY }; 378 int[] p2 = new int[] { x, y }; 379 380 int[] p = findPointWithDistance( p0, p1, p2, w ); 381 x = p[0]; 382 y = p[1]; 383 384 lastX = x; 385 lastY = y; 386 int boxEndX = x; 387 int boxEndY = y; 388 389 // does the linesegment run from right to left? 390 if ( x <= boxStartX ) { 391 boxEndX = boxStartX; 392 boxEndY = boxStartY; 393 boxStartX = x; 394 boxStartY = y; 395 x = boxEndX; 396 y = boxEndY; 397 } 398 399 double rotation = getRotation( boxStartX, boxStartY, x, y ); 400 double[] deviation = calcDeviation( new int[] { boxStartX, boxStartY }, new int[] { boxEndX, boxEndY }, 401 eCandidates ); 402 403 Label label = null; 404 405 switch ( placementType ) { 406 case LinePlacement.TYPE_ABSOLUTE: { 407 label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature, 408 symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height, 409 rotation, 0.0, 0.5, ( w - width ) / 2, perpendicularOffset ); 410 break; 411 } 412 case LinePlacement.TYPE_ABOVE: { 413 label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature, 414 symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height, 415 rotation, 0.0, 0.5, ( w - width ) / 2, delta + deviation[0] ); 416 break; 417 } 418 case LinePlacement.TYPE_BELOW: 419 case LinePlacement.TYPE_AUTO: { 420 label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature, 421 symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height, 422 rotation, 0.0, 0.5, ( w - width ) / 2, -delta - deviation[1] ); 423 break; 424 } 425 case LinePlacement.TYPE_CENTER: { 426 label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature, 427 symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height, 428 rotation, 0.0, 0.5, ( w - width ) / 2, 0.0 ); 429 break; 430 } 431 default: { 432 } 433 } 434 labels.add( label ); 435 boxStartX = lastX; 436 boxStartY = lastY; 437 eCandidates.clear(); 438 } else { 439 eCandidates.add( new int[] { x, y } ); 440 lastX = x; 441 lastY = y; 442 i++; 443 } 444 } 445 446 // pick lists of boxes on the linestring 447 ArrayList<Label> pick = new ArrayList<Label>( 100 ); 448 int n = labels.size(); 449 for ( int j = n / 2; j < labels.size(); j += ( gap + 1 ) ) { 450 pick.add( labels.get( j ) ); 451 } 452 for ( int j = n / 2 - ( gap + 1 ); j > 0; j -= ( gap + 1 ) ) { 453 pick.add( labels.get( j ) ); 454 } 455 return pick; 456 } 457 458 /** 459 * Calculates the maximum deviation that points on a linestring have to the ideal line between 460 * the starting point and the end point. 461 * <p> 462 * The ideal line is thought to be running from left to right, the left deviation value 463 * generally is above the line, the right value is below. 464 * <p> 465 * 466 * @param start 467 * starting point of the linestring 468 * @param end 469 * end point of the linestring 470 * @param points 471 * points in between 472 * @return maximum deviation 473 */ 474 public static double[] calcDeviation( int[] start, int[] end, List<int[]> points ) { 475 476 // extreme deviation to the left 477 double d1 = 0.0; 478 // extreme deviation to the right 479 double d2 = 0.0; 480 Iterator it = points.iterator(); 481 482 // eventually swap start and end point 483 if ( start[0] > end[0] ) { 484 int[] tmp = start; 485 start = end; 486 end = tmp; 487 } 488 489 if ( start[0] != end[0] ) { 490 // label orientation is not completly vertical 491 if ( start[1] != end[1] ) { 492 // label orientation is not completly horizontal 493 while ( it.hasNext() ) { 494 int[] point = (int[]) it.next(); 495 double u = ( (double) end[1] - (double) start[1] ) / ( (double) end[0] - (double) start[0] ); 496 double x = ( u * u * start[0] - u * ( (double) start[1] - (double) point[1] ) + point[0] ) 497 / ( 1.0 + u * u ); 498 double y = ( x - start[0] ) * u + start[1]; 499 double d = getDistance( point, new int[] { (int) ( x + 0.5 ), (int) ( y + 0.5 ) } ); 500 if ( y >= point[1] ) { 501 // candidate for left extreme value 502 if ( d > d1 ) { 503 d1 = d; 504 } 505 } else if ( d > d2 ) { 506 // candidate for right extreme value 507 d2 = d; 508 } 509 } 510 } else { 511 // label orientation is completly horizontal 512 while ( it.hasNext() ) { 513 int[] point = (int[]) it.next(); 514 double d = point[1] - start[1]; 515 if ( d < 0 ) { 516 // candidate for left extreme value 517 if ( -d > d1 ) { 518 d1 = -d; 519 } 520 } else if ( d > d2 ) { 521 // candidate for left extreme value 522 d2 = d; 523 } 524 } 525 } 526 } else { 527 // label orientation is completly vertical 528 while ( it.hasNext() ) { 529 int[] point = (int[]) it.next(); 530 double d = point[0] - start[0]; 531 if ( d < 0 ) { 532 // candidate for left extreme value 533 if ( -d > d1 ) { 534 d1 = -d; 535 } 536 } else if ( d > d2 ) { 537 // candidate for right extreme value 538 d2 = d; 539 } 540 } 541 } 542 return new double[] { d1, d2 }; 543 } 544 545 /** 546 * Finds a point on the line between p1 and p2 that has a certain distance from point p0 547 * (provided that there is such a point). 548 * <p> 549 * 550 * @param p0 551 * point that is used as reference point for the distance 552 * @param p1 553 * starting point of the line 554 * @param p2 555 * end point of the line 556 * @param d 557 * distance 558 * @return point on the line between p1 and p2 that has a certain distance from point p0 559 */ 560 public static int[] findPointWithDistance( int[] p0, int[] p1, int[] p2, int d ) { 561 562 double x, y; 563 double x0 = p0[0]; 564 double y0 = p0[1]; 565 double x1 = p1[0]; 566 double y1 = p1[1]; 567 double x2 = p2[0]; 568 double y2 = p2[1]; 569 570 if ( x1 != x2 ) { 571 // line segment does not run vertical 572 double u = ( y2 - y1 ) / ( x2 - x1 ); 573 double p = -2 * ( x0 + u * u * x1 - u * ( y1 - y0 ) ) / ( u * u + 1 ); 574 double q = ( ( y1 - y0 ) * ( y1 - y0 ) + u * u * x1 * x1 + x0 * x0 - 2 * u * x1 * ( y1 - y0 ) - d * d ) 575 / ( u * u + 1 ); 576 double minX = x1; 577 double maxX = x2; 578 double minY = y1; 579 double maxY = y2; 580 if ( minX > maxX ) { 581 minX = x2; 582 maxX = x1; 583 } 584 if ( minY > maxY ) { 585 minY = y2; 586 maxY = y1; 587 } 588 x = -p / 2 - Math.sqrt( ( p / 2 ) * ( p / 2 ) - q ); 589 if ( x < minX || x > maxX ) { 590 x = -p / 2 + Math.sqrt( ( p / 2 ) * ( p / 2 ) - q ); 591 } 592 y = ( x - x1 ) * u + y1; 593 } else { 594 // vertical line segment 595 x = x1; 596 double minY = y1; 597 double maxY = y2; 598 599 if ( minY > maxY ) { 600 minY = y2; 601 maxY = y1; 602 } 603 604 double p = -2 * y0; 605 double q = y0 * y0 + ( x1 - x0 ) * ( x1 - x0 ) - d * d; 606 607 y = -p / 2 - Math.sqrt( ( p / 2 ) * ( p / 2 ) - q ); 608 if ( y < minY || y > maxY ) { 609 y = -p / 2 + Math.sqrt( ( p / 2 ) * ( p / 2 ) - q ); 610 } 611 } 612 return new int[] { (int) ( x + 0.5 ), (int) ( y + 0.5 ) }; 613 } 614 615 /** 616 * @param x1 617 * @param y1 618 * @param x2 619 * @param y2 620 * @return rotation (degree) of the line between two passed coordinates 621 */ 622 public static double getRotation( double x1, double y1, double x2, double y2 ) { 623 double dx = x2 - x1; 624 double dy = y2 - y1; 625 626 return Math.toDegrees( Math.atan( dy / dx ) ); 627 } 628 629 /** 630 * @param p1 631 * @param p2 632 * @return distance between two passed coordinates 633 */ 634 public static double getDistance( int[] p1, int[] p2 ) { 635 double dx = p1[0] - p2[0]; 636 double dy = p1[1] - p2[1]; 637 return Math.sqrt( dx * dx + dy * dy ); 638 } 639 640 /** 641 * @param x1 642 * @param y1 643 * @param x2 644 * @param y2 645 * @return distance between two passed coordinates 646 */ 647 public static double getDistance( double x1, double y1, double x2, double y2 ) { 648 double dx = x2 - x1; 649 double dy = y2 - y1; 650 return Math.sqrt( dx * dx + dy * dy ); 651 } 652 653 /** 654 * Calculates the screen coordinates of the given <tt>Curve</tt>. 655 * physical screen coordinates 656 */ 657 public static int[][] calcScreenCoordinates( GeoTransform projection, Curve curve ) { 658 659 LineString lineString = null; 660 try { 661 lineString = curve.getAsLineString(); 662 } catch ( GeometryException e ) { 663 } 664 665 int count = lineString.getNumberOfPoints(); 666 int[][] pos = new int[3][]; 667 pos[0] = new int[count]; 668 pos[1] = new int[count]; 669 pos[2] = new int[1]; 670 671 int k = 0; 672 for ( int i = 0; i < count; i++ ) { 673 Position position = lineString.getPositionAt( i ); 674 double tx = projection.getDestX( position.getX() ); 675 double ty = projection.getDestY( position.getY() ); 676 if ( i > 0 ) { 677 if ( getDistance( tx, ty, pos[0][k - 1], pos[1][k - 1] ) > 1 ) { 678 pos[0][k] = (int) ( tx + 0.5 ); 679 pos[1][k] = (int) ( ty + 0.5 ); 680 k++; 681 } 682 } else { 683 pos[0][k] = (int) ( tx + 0.5 ); 684 pos[1][k] = (int) ( ty + 0.5 ); 685 k++; 686 } 687 } 688 pos[2][0] = k; 689 690 return pos; 691 } 692 693 /** 694 * Returns the physical (screen) coordinates. 695 * @return physical screen coordinates 696 */ 697 public static int[] calcScreenCoordinates( GeoTransform projection, Geometry geometry ) { 698 699 int[] coords = new int[2]; 700 701 Position source = null; 702 if ( geometry instanceof Point ) { 703 source = ( (Point) geometry ).getPosition(); 704 } else if ( geometry instanceof Curve || geometry instanceof MultiCurve ) { 705 source = geometry.getCentroid().getPosition(); 706 } else { 707 source = geometry.getCentroid().getPosition(); 708 } 709 710 coords[0] = (int) ( projection.getDestX( source.getX() ) + 0.5 ); 711 coords[1] = (int) ( projection.getDestY( source.getY() ) + 0.5 ); 712 return coords; 713 } 714 }