How can I create a gradient in PDFBox? Or maybe "can I?".
I don't want to create them and export to jpeg or something else. I need a light document, so this has to be programmed somehow.
Any ideas?
After a lot of research, I finally created a small "creator of my own gradient"! It looks like this:
COSDictionary fdict = new COSDictionary();
fdict.setInt(COSName.FUNCTION_TYPE, 2); // still not understaning that...
COSArray domain = new COSArray();
domain.add(COSInteger.get(0));
domain.add(COSInteger.get(1));
COSArray c0 = new COSArray();
c0.add(COSFloat.get("0.64176"));
c0.add(COSFloat.get("0.72588"));
c0.add(COSFloat.get("0.78078"));
COSArray c1 = new COSArray();
c1.add(COSFloat.get("0.57176"));
c1.add(COSFloat.get("0.62588"));
c1.add(COSFloat.get("0.70078"));
fdict.setItem(COSName.DOMAIN, domain);
fdict.setItem(COSName.C0, c0);
fdict.setItem(COSName.C1, c1);
fdict.setInt(COSName.N, 1);
PDFunctionType2 func = new PDFunctionType2(fdict);
PDShadingType2 axialShading = new PDShadingType2(new COSDictionary());
axialShading.setColorSpace(PDDeviceRGB.INSTANCE);
axialShading.setShadingType(PDShading.SHADING_TYPE2);
COSArray coords1 = new COSArray();
coords1.add(COSInteger.get(0));
coords1.add(COSInteger.get(0));
coords1.add(COSInteger.get(850)); // size of my page
coords1.add(COSInteger.get(600));
axialShading.setCoords(coords1); // so this sets the bounds of my gradient
axialShading.setFunction(func); // and this determines all the curves etc?
CStr.shadingFill(axialShading); // where CStr is a ContentStream for my PDDocument
I will leave this for others. Leave your opinions and be free to show me some clever ideas to improve this code :)
Here's a class I made to make the creation of gradients easier. It supports axial gradients with multiple colors. It uses java.awt.Color to specify colors but that can be replaced easily.
public class PDGradient extends PDShadingType2 {
public PDGradient(List<GradientPart> parts) {
super(new COSDictionary());
// PDF 1.7 - 8.7.4.5.3 Type 2 (Axial) Shadings
setColorSpace(PDDeviceRGB.INSTANCE);
setShadingType(PDShadingType2.SHADING_TYPE2);
setFunction(createGradientFunction(parts));
}
private static PDFunction createGradientFunction(List<GradientPart> parts) {
if (parts.size() < 2) {
throw new IllegalArgumentException("Gradient must have at least 2 colors.");
}
GradientPart first = parts.get(0);
GradientPart last = parts.get(parts.size() - 1);
if (first.ratio != 0f) {
throw new IllegalArgumentException("Gradient first color ratio must be 0.");
} else if (last.ratio != 1f) {
throw new IllegalArgumentException("Gradient last color ratio must be 1.");
}
if (parts.size() == 2) {
// Only two colors, use exponential function.
return createColorFunction(first.color, last.color);
}
// Multiple colors, use stitching function to combine exponential functions
// PDF 1.7 - 7.10.4 Type 3 (Stitching) Functions
COSDictionary dict = new COSDictionary();
COSArray functions = new COSArray();
COSArray bounds = new COSArray();
COSArray encode = new COSArray();
GradientPart lastPart = first;
for (int i = 1; i < parts.size(); i++) {
GradientPart part = parts.get(i);
// Add exponential function for interpolating between these two colors.
functions.add(createColorFunction(lastPart.color, part.color));
// Specify function bounds, except for first and last, which are specified by domain.
if (i != parts.size() - 1) {
bounds.add(new COSFloat(part.ratio));
}
// Used to interpolate stitching function subdomain (eg: [0.2 0.5]
// to the exponential function domain, which is always [0.0 1.0].
encode.add(COSInteger.ZERO);
encode.add(COSInteger.ONE);
lastPart = part;
}
dict.setInt(COSName.FUNCTION_TYPE, 3);
dict.setItem(COSName.DOMAIN, new PDRange()); // [0.0 1.0]
dict.setItem(COSName.FUNCTIONS, functions);
dict.setItem(COSName.BOUNDS, bounds);
dict.setItem(COSName.ENCODE, encode);
return new PDFunctionType3(dict);
}
private static PDFunction createColorFunction(Color start, Color end) {
// PDF 1.7 - 7.10.3 Type 2 (Exponential Interpolation) Functions
COSDictionary dict = new COSDictionary();
dict.setInt(COSName.FUNCTION_TYPE, 2);
dict.setItem(COSName.DOMAIN, new PDRange()); // [0.0 1.0]
dict.setItem(COSName.C0, createColorCOSArray(start));
dict.setItem(COSName.C1, createColorCOSArray(end));
dict.setInt(COSName.N, 1); // Linear interpolation
return new PDFunctionType2(dict);
}
private static COSArray createColorCOSArray(Color color) {
// Create a COSArray for a color.
// java.awt.Color uses 0-255 values while PDF uses 0-1.
COSArray a = new COSArray();
a.add(new COSFloat(color.getRed() / 255f));
a.add(new COSFloat(color.getGreen() / 255f));
a.add(new COSFloat(color.getBlue() / 255f));
return a;
}
/**
* Specifies a color and its position in a {#link PDGradient}.
*/
public static class GradientPart {
public final Color color;
public final float ratio;
public GradientPart(Color color, float ratio) {
this.color = color;
this.ratio = ratio;
}
}
}
Example usage:
List<GradientPart> parts = new ArrayList<>();
parts.add(new GradientPart(Color.RED, 0.0f));
parts.add(new GradientPart(Color.YELLOW, 0.5f));
parts.add(new GradientPart(Color.GREEN, 1.0f));
PDGradient gradient = new PDGradient(parts);
gradient.setCoords(...);
pdfStream.shadingFill(gradient)
This works essentially the same as the other answer for two colors gradients, using an exponential function (type 2) to linearly interpolate between two colors. If there are more colors, a stitching (type 3) function is used to combine multiple exponential functions with different subdomains.
Related
I am using itextpdf 7 (7.2.0) to create a pdf file. However even though the TOC part is rendered very well, in the content part the text overflows. Here is my code that generates the pdf:
public class Main {
public static void main(String[] args) throws IOException {
PdfWriter writer = new PdfWriter("fiftyfourthPdf.pdf");
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf, PageSize.A4,false);
//document.setMargins(30,10,36,10);
// Create a PdfFont
PdfFont font = PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN,"Cp1254");
document
.setTextAlignment(TextAlignment.JUSTIFIED)
.setFont(font)
.setFontSize(11);
PdfOutline outline = null;
java.util.List<AbstractMap.SimpleEntry<String, AbstractMap.SimpleEntry<String, Integer>>> toc = new ArrayList<>();
for(int i=0;i<5000;i++){
String line = "This is paragraph " + String.valueOf(i+1)+ " ";
line = line.concat(line).concat(line).concat(line).concat(line).concat(line);
Paragraph p = new Paragraph(line);
p.setKeepTogether(true);
document.add(p.setFont(font).setFontSize(10).setHorizontalAlignment(HorizontalAlignment.CENTER).setTextAlignment(TextAlignment.LEFT));
//PROCESS FOR TOC
String name = "para " + String.valueOf(i+1);
outline = createOutline(outline,pdf,line ,name );
AbstractMap.SimpleEntry<String, Integer> titlePage = new AbstractMap.SimpleEntry(line, pdf.getNumberOfPages());
p
.setFont(font)
.setFontSize(12)
//.setKeepWithNext(true)
.setDestination(name)
// Add the current page number to the table of contents list
.setNextRenderer(new UpdatePageRenderer(p));
toc.add(new AbstractMap.SimpleEntry(name, titlePage));
}
int contentPageNumber = pdf.getNumberOfPages();
for (int i = 1; i <= contentPageNumber; i++) {
// Write aligned text to the specified by parameters point
document.showTextAligned(new Paragraph(String.format("Sayfa %s / %s", i, contentPageNumber)).setFontSize(10),
559, 26, i, TextAlignment.RIGHT, VerticalAlignment.MIDDLE, 0);
}
//BEGINNING OF TOC
document.add(new AreaBreak());
Paragraph p = new Paragraph("Table of Contents")
.setFont(font)
.setDestination("toc");
document.add(p);
java.util.List<TabStop> tabStops = new ArrayList<>();
tabStops.add(new TabStop(580, TabAlignment.RIGHT, new DottedLine()));
for (AbstractMap.SimpleEntry<String, AbstractMap.SimpleEntry<String, Integer>> entry : toc) {
AbstractMap.SimpleEntry<String, Integer> text = entry.getValue();
p = new Paragraph()
.addTabStops(tabStops)
.add(text.getKey())
.add(new Tab())
.add(String.valueOf(text.getValue()))
.setAction(PdfAction.createGoTo(entry.getKey()));
document.add(p);
}
// Move the table of contents to the first page
int tocPageNumber = pdf.getNumberOfPages();
for (int i = 1; i <= tocPageNumber; i++) {
// Write aligned text to the specified by parameters point
document.showTextAligned(new Paragraph("\n footer text\n second line\nthird line").setFontColor(ColorConstants.RED).setFontSize(8),
300, 26, i, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
}
document.flush();
for(int z = 0; z< (tocPageNumber - contentPageNumber ); z++){
pdf.movePage(tocPageNumber,1);
pdf.getPage(1).setPageLabel(PageLabelNumberingStyle.UPPERCASE_LETTERS,
null, 1);
}
//pdf.movePage(tocPageNumber, 1);
// Add page labels
/*pdf.getPage(1).setPageLabel(PageLabelNumberingStyle.UPPERCASE_LETTERS,
null, 1);*/
pdf.getPage(tocPageNumber - contentPageNumber + 1).setPageLabel(PageLabelNumberingStyle.DECIMAL_ARABIC_NUMERALS,
null, 1);
document.close();
}
private static PdfOutline createOutline(PdfOutline outline, PdfDocument pdf, String title, String name) {
if (outline == null) {
outline = pdf.getOutlines(false);
outline = outline.addOutline(title);
outline.addDestination(PdfDestination.makeDestination(new PdfString(name)));
} else {
PdfOutline kid = outline.addOutline(title);
kid.addDestination(PdfDestination.makeDestination(new PdfString(name)));
}
return outline;
}
private static class UpdatePageRenderer extends ParagraphRenderer {
protected AbstractMap.SimpleEntry<String, Integer> entry;
public UpdatePageRenderer(Paragraph modelElement, AbstractMap.SimpleEntry<String, Integer> entry) {
super(modelElement);
this.entry = entry;
}
public UpdatePageRenderer(Paragraph modelElement) {
super(modelElement);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
//entry.setValue(layoutContext.getArea().getPageNumber());
if (result.getStatus() != LayoutResult.FULL) {
if (null != result.getOverflowRenderer()) {
result.getOverflowRenderer().setProperty(
Property.LEADING,
result.getOverflowRenderer().getModelElement().getDefaultProperty(Property.LEADING));
} else {
// if overflow renderer is null, that could mean that the whole renderer will overflow
setProperty(
Property.LEADING,
result.getOverflowRenderer().getModelElement().getDefaultProperty(Property.LEADING));
}
}
return result;
}
#Override
// If not overriden, the default renderer will be used for the overflown part of the corresponding paragraph
public IRenderer getNextRenderer() {
return new UpdatePageRenderer((Paragraph) this.getModelElement());
}
}
}
Here are the screen shots of TOC part and content part :
TOC :
Content :
What am I missing? Thank you all for your help.
UPDATE
When I add the line below it renders with no overflow but the page margins of TOC and content part differ (the TOC margin is way more than the content margin). See the picture attached please :
document.setMargins(30,60,36,20);
Right Margin difference between TOC and content:
UPDATE 2 :
When I comment the line
document.setMargins(30,60,36,20);
and set the font size on line :
document.add(p.setFont(font).setFontSize(10).setHorizontalAlignment(HorizontalAlignment.CENTER).setTextAlignment(TextAlignment.LEFT));
to 12 then it renders fine. What difference should possibly the font size cause for the page content and margins? Are not there standard page margins and page setups? Am I unknowingly (I am newbie to itextpdf) messing some standard implementations?
TL; DR: either remove setFontSize in
p
.setFont(font)
.setFontSize(12)
//.setKeepWithNext(true)
.setDestination(name)
or change setFontSize(10) -> setFontSize(12) in
document.add(p.setFont(font).setFontSize(10).setHorizontalAlignment(HorizontalAlignment.CENTER).setTextAlignment(TextAlignment.LEFT));
Explanation: You are setting the Document to not immediately flush elements added to that document with the following line:
Document document = new Document(pdf, PageSize.A4,false);
Then you add an paragraph element with font size equal to 10 to the document with the following line:
document.add(p.setFont(font).setFontSize(10).setHorizontalAlignment(HorizontalAlignment.CENTER).setTextAlignment(TextAlignment.LEFT));
What happens is that the element is being laid out (split in lines etc), but now drawn on the page. Then you do .setFontSize(12) and this new font size is applied for draw only, so iText calculated that X characters would fit into one line assuming the font size is 10 while in reality the font size is 12 and obviously fewer characters can fit into one line.
There is no sense in setting the font size two times to different values - just pick one value you want to see in the resultant document and set it once.
I want to plot a line which connects 2 points on a map.
The code I am using:
public class Quickstart {
public static void main(String[] args) throws Exception {
// display a data store file chooser dialog for shapefiles
File file = JFileDataStoreChooser.showOpenFile("shp", null);
if (file == null) {
return;
}
FileDataStore store = FileDataStoreFinder.getDataStore(file);
SimpleFeatureSource featureSource = store.getFeatureSource();
GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
// ask for current and destination positions
double latitude, longitude, latitudeDest, longitudeDest;
Scanner reader = new Scanner(System.in);
reader.useLocale(Locale.US);
System.out.println("Enter reference longitude and latitude:\n");
longitude = reader.nextDouble();
latitude = reader.nextDouble();
System.out.println("Enter destination longitude and latitude:\n");
longitudeDest = reader.nextDouble();
latitudeDest = reader.nextDouble();
reader.close();
final String EPSG4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\"," +
"\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\", " +
"0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]";
CoordinateReferenceSystem crs = CRS.parseWKT(EPSG4326);
Point start = gf.createPoint(new Coordinate(longitude, latitude));
Point end = gf.createPoint(new Coordinate(longitudeDest, latitudeDest));
GeodeticCalculator gc = new GeodeticCalculator(crs);
gc.setStartingPosition(JTS.toDirectPosition(start.getCoordinate(), crs));
gc.setDestinationPosition(JTS.toDirectPosition(end.getCoordinate(), crs));
// Calculate distance between points
double distance = gc.getOrthodromicDistance();
int totalmeters = (int) distance;
int km = totalmeters / 1000;
int meters = totalmeters - (km * 1000);
float remaining_cm = (float) (distance - totalmeters) * 10000;
remaining_cm = Math.round(remaining_cm);
float cm = remaining_cm / 100;
System.out.println("Distance = " + km + "km " + meters + "m " + cm + "cm");
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("TwoDistancesType");
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("location", Point.class);
// build the type
final SimpleFeatureType TYPE = builder.buildFeatureType();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
featureBuilder.add(start);
//featureBuilder.add(end);
SimpleFeature feature = featureBuilder.buildFeature(null);
DefaultFeatureCollection featureCollection = new DefaultFeatureCollection("internal", TYPE);
featureCollection.add(feature);
Style style = SLD.createSimpleStyle(TYPE, Color.red);
Layer layer = new FeatureLayer(featureCollection, style);
// Create a map content and add our shapefile to it
MapContent map = new MapContent();
map.setTitle("TEST");
map.addLayer(layer);
// Now display the map
JMapFrame.showMap(map);
}
}
I have 2 problems:
1) I can't add a second feature to featureBuilder.It doesn't allow it.It shows Can handle 1 attributes only, index is 1.
So, how can I plot a line then?
2) With the above code, I am receiving:
org.geotools.renderer.lite.StreamingRenderer fireErrorEvent SEVERE: The scale denominator must be positive
java.lang.IllegalArgumentException: The scale denominator must be positive
------- UPDATE ------------------------
After the solution that #Michael gave for the first question , now I no longer receive the error regarding the denominator, but I am receiving an empty map (white space).
----- UPDATE according to #iant suggestion ----------------
So, I tried this.Created a coordinates which holds the coordinates of the points (start and end) ,then created a linestring with those coordinates and added it to featurebuilder.
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("TwoDistancesType");
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("line", LineString.class); //added a linestring class
final SimpleFeatureType TYPE = builder.buildFeatureType();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
Coordinate[] coordinates = {start.getCoordinate(), end.getCoordinate()};
LineString line = gf.createLineString(coordinates);
featureBuilder.add(line);
and even though I am loading a map (countries.shp) it shows me an empty white space with a red line.
------ SOLUTION -------------
Ok, so the solution is (thanks to #iants comments):
Style style = SLD.createLineStyle(Color.red, 2.0f);
Layer layer = new FeatureLayer(featureCollection, style);
// Create style for the file
Style shpStyle = SLD.createSimpleStyle(TYPE, Color.blue);
Layer shpLayer = new FeatureLayer(featureSource, shpStyle);
// Create a map content and add our shapefile to it
MapContent map = new MapContent();
map.setTitle("TEST");
map.addLayer(layer);
map.addLayer(shpLayer);
and now you have a red line on a blue map!
You need to create a LineString from your points and then store that in your feature.
You should then get a correct scale but you might want to add some other data such as a coast line to the map first. The quick start tutorial can show you how to do that.
Disclaimer: I've never used geotools.
Looking at the source code of SimpleFeatureBuilder, add calls set which throws that error if:
if(index >= values.length)
throw new ArrayIndexOutOfBoundsException("Can handle "
+ values.length + " attributes only, index is " + index);
values is populated here:
values = new Object[featureType.getAttributeCount()];
so it's obvious that that problem is because your Type only has one property. Change it so it has two:
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("TwoDistancesType");
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("start", Point.class);
builder.add("end", Point.class);
I am trying to extract the bounding boxes of each word from the javacpp tesseract. This appears to be the bounding box call (my full code below):
boolean box = ri.BoundingBox(RIL_WORD, coord1, coord2, coord3, coord4)
RIL_WORD is the iterator level that can be adjusted for words, sentences, and paragraphs. The coordinates are IntPointers (included class with javacpp).
The api says this returns the bounding box coordinates but returns a boolean instead. SO at this point I know there is a bounding box but still cannot get the actual coordinates. Does anyone know how to get the bounding box rectangles out of java cpp tessaract? Thanks for the help. I have posted my working code for getting the individual words and the confidence level below because I had such a hard time finding examples.
public class TesseractOCR {
public void OCRText() {
BytePointer outText;
TessBaseAPI api = new TessBaseAPI();
// Initialize tesseract-ocr with English, without specifying tessdata path
if (api.Init(null, "eng") != 0) {
System.err.println("Could not initialize tesseract.");
System.exit(1);
}
// Open input image with leptonica library
org.bytedeco.javacpp.lept.PIX image = pixRead("testimage.png");
// Get OCR result
outText = api.GetUTF8Text();
System.out.println("OCR output:\n" + outText.getString());
final ResultIterator ri = api.GetIterator();
int x1 = 0;
int y1 = 0;
int x2 = 0;
int y2 = 0;
IntPointer coord1 = new IntPointer(x1);
IntPointer coord2 = new IntPointer(y1);
IntPointer coord3 = new IntPointer(x2);
IntPointer coord4 = new IntPointer(y2);
ri.Begin();
if (ri !=null) {
do {
BytePointer word = ri.GetUTF8Text(RIL_WORD);
float conf = ri.Confidence(RIL_WORD);
boolean box = ri.BoundingBox(RIL_WORD, coord1, coord2, coord3, coord4);
System.out.println(word.getString());
System.out.println(conf);
System.out.println(box);
} while (ri.Next(RIL_WORD));
}
api.End();
outText.deallocate();
pixDestroy(image);
}
}
I need to use the Mediabox to get the coordinates in a page from a pdf, but for some pdfs I get null and for others I get the regular Mediabox.
Why is it happen? How is the method work?
private void addPDF(File pdf) throws IOException, InterruptedException {
waiting_label.setText("");
pdf_name.setText(pdf.getName());
all_my_p = new ArrayList<>();
System.out.println("prova.JPanelImageAndButton.addPDF()");
/*pddoc = null;
cosdoc = null;*/
PDFParser parser = new PDFParser(new FileInputStream(pdf));
parser.parse();
cosdoc = parser.getDocument();
pddoc = new PDDocument(cosdoc);
List<PDPage> list = pddoc.getDocumentCatalog().getAllPages();
pdf_name.setText(pdf.getName());
if (my_p != null) {
remove(my_p);
}
JFrame top = (JFrame) SwingUtilities.getWindowAncestor(this);
Dimension d = new Dimension(top.getWidth(), top.getHeight() - p.getHeight());
for (int i = 0; i < n_page; i++) {
PDPage pdp=list.get(i);
System.out.println("prova.JPanelImageAndButton.addPDF()"+pdp.getMediaBox());
final MyPanelFrame t = new MyPanelFrame(pdf.getName() + "_temp" + (i + 1) + ".png", pdp);
t.setPreferredSize(d);
t.setBounds(new Rectangle(10, 30, top.getWidth(), top.getHeight()));
t.addHierarchyBoundsListener(new HierarchyBoundsListener() {
#Override
public void ancestorMoved(HierarchyEvent e) {
}
#Override
public void ancestorResized(HierarchyEvent e) {
t.setPreferredSize(new Dimension(top.getWidth(), top.getHeight() - p.getHeight()));
t.setBounds(new Rectangle(10, 30, top.getWidth(), top.getWidth()));
top.revalidate();
}
});
all_my_p.add(t);
}
my_p = all_my_p.get(0);
add(my_p);
top.setSize(top.getWidth() + 1, top.getHeight() + 1);
top.revalidate();
top.setSize(top.getWidth() - 1, top.getHeight() - 1);
top.revalidate();
top.setExtendedState(JFrame.MAXIMIZED_BOTH);
label_load.setText("");
label_save.setText("");
activityDone = true;
//pddoc.close();
//cosdoc.close();
}
This is an example, but for the same pdf I get null everywhere I use getMediaBox().
You seem to use a 1.x.x version of PDFBox. For these versions the observed behavior is to be expected, cf. the JavaDocs of the method:
/**
* A rectangle, expressed
* in default user space units, defining the boundaries of the physical
* medium on which the page is intended to be displayed or printed
*
* This will get the MediaBox at this page and not look up the hierarchy.
* This attribute is inheritable, and findMediaBox() should probably used.
* This will return null if no MediaBox are available at this level.
*
* #return The MediaBox at this level in the hierarchy.
*/
public PDRectangle getMediaBox()
This comment also presents the solution, use findMediaBox() instead:
/**
* This will find the MediaBox for this page by looking up the hierarchy until
* it finds them.
*
* #return The MediaBox at this level in the hierarchy.
*/
public PDRectangle findMediaBox()
If you plan to switch to PDFBox 2.0.0, you'll find that the behavior of getMediaBox has changed, it already walks the hierarchy if necessary and there is no findMediaBox anymore.
I'm new to the GeoTools library for Java and I'm simply trying to draw a polygon on a map. I'm using GPS coordinates for points, which draw just fine, but I just can't figure out how to draw the LineString between them to save my life.
I have checked all the tutorials on geotools.org and also this posting but no avail.
Should this be so complicated? Can anybody maybe post the code fragments required to draw a LineString? This is what I've tried last:
SimpleFeatureType lineType = DataUtilities.createType("LINE", "geom:LineString,name:String");
SimpleFeatureBuilder featureBuilderLines = new SimpleFeatureBuilder(lineType);
SimpleFeatureCollection collectionLines = FeatureCollections.newCollection();
LineString line = builder.createLineString(listOfPoints);
featureBuilderLines.add(line);
SimpleFeature featureLine = featureBuilderLines.buildFeature(null);
((DefaultFeatureCollection)collectionLines).add(featureLine);
Style lineStyle = SLD.createLineStyle(Color.RED, 2.0f);
map.addLayer(new FeatureLayer(collectionLines, lineStyle));
Thanks and advance and best regards
You seem to be mixing Geometry types, try something like:
import org.geotools.geometry.jts.JTSFactoryFinder;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LineString;
public class TestLineBuilder {
public static void main(String[] args) {
com.vividsolutions.jts.geom.GeometryFactory gFac = JTSFactoryFinder.getGeometryFactory();
Coordinate[] coordinates = new Coordinate[2];
coordinates[0] = new Coordinate(1,3);
coordinates[1] = new Coordinate(3,8);
LineString line =gFac.createLineString(coordinates );
System.out.println(line);
}
}
which gives the right sort of answer for me.
Have been fighting with this. Finally got it to work for saving the map to an image (png) using a mixture of different snippets from the web. Showing the map via JMapFrame.showMap(map); leads to an Exception and crash. Anyway, I needed the image. The example for a line with two points is shown below. Adding a polyline with more points should be identical:
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
b.setName( "LineFeature" );
//add a geometry property
b.setCRS( DefaultGeographicCRS.WGS84 ); // set crs first
b.add( "line", LineString.class ); // then add geometry
//build the type
final SimpleFeatureType TYPE = b.buildFeatureType();
SimpleFeatureBuilder featureBuilderLines = new SimpleFeatureBuilder(TYPE);
SimpleFeatureCollection collectionLines = new DefaultFeatureCollection("internal",TYPE);
GeometryFactory gFac = JTSFactoryFinder.getGeometryFactory(JTSFactoryFinder.EMPTY_HINTS);
Coordinate[] coordinates = new Coordinate[2];
double latStart = 44.9;
double lonStart = 14.9;
double latEnd = 12.1;
double lonEnd = 9.4;
coordinates[0] = new Coordinate(lonStart, latStart);
coordinates[1] = new Coordinate(lonEnd, latEnd);
LineString line = gFac.createLineString(coordinates );
featureBuilderLines.add(line);
SimpleFeature featureLine = featureBuilderLines.buildFeature(null);
collectionLines.add(featureLine);
float lineWidt = 2.0f;
Style lineStyle = SLD.createLineStyle(Color.red, lineWidth);
SimpleFeatureSource collectionFeatureSource = new CollectionFeatureSource(collectionLines);
map.addLayer(collectionFeatureSource, lineStyle);