Latitude and longitude values from sqlgeometry linestring using C# - latitude-longitude

I recently discovered spatial columns in Microsoft SQL.
I have now successfully stored LINESTRING sqlgeometry with correct EPSG (STRID). Checked by using AutoCAD Map.
The data is now sqlgeometry LINESTRINGs containing groups of X Y coordinates.
Using C# (or an sql statement), how do I get latitude and longitude values of the coordinate points?

What you need is reprojection onto spherical coordinates.
SQL Server does not provides such functionality. You need some plumbing in C# to get your geometries and project coordinates.
If you have your conversion function, using a GeometrySink is fast and would help.
internal class SqlGeometryProjectionSink : IGeometrySink110
{
IGeometrySink110 _sink;
int _outSrid;
Func<double, double, double[]> _coordTransform;
public SqlGeometryProjectionSink(IGeometrySink110 p_Sink, int outSrid, Func<double, double, double[]> coordTransform)
{
_sink = p_Sink;
_outSrid = outSrid;
_coordTransform = coordTransform;
if (_coordTransform == null)
{
_coordTransform = new Func<double, double, double[]>((x, y) => new double[] { x, y });
}
}
void IGeometrySink.AddLine(double x, double y, double? z, double? m)
{
double[] proj = _coordTransform(x, y);
_sink.AddLine(proj[0], proj[1], z, m);
}
void IGeometrySink.BeginFigure(double x, double y, double? z, double? m)
{
double[] proj = _coordTransform(x, y);
_sink.BeginFigure(proj[0], proj[1], z, m);
}
void IGeometrySink.BeginGeometry(OpenGisGeometryType type)
{
_sink.BeginGeometry(type);
}
void IGeometrySink.EndFigure()
{
_sink.EndFigure();
}
void IGeometrySink.EndGeometry()
{
_sink.EndGeometry();
}
void IGeometrySink.SetSrid(int srid)
{
_sink.SetSrid(_outSrid);
}
public static SqlGeometry ReprojectGeometry(SqlGeometry geom, int srid, Func<double, double, double[]> coordTransform)
{
if (geom != null)
{
SqlGeometryBuilder builder = new SqlGeometryBuilder();
SqlGeometryProjectionSink sink = new SqlGeometryProjectionSink(builder, srid, coordTransform);
geom.Populate(sink);
return builder.ConstructedGeometry;
}
return null;
}
void IGeometrySink110.AddCircularArc(double x1, double y1, double? z1, double? m1, double x2, double y2, double? z2, double? m2)
{
throw new NotImplementedException();
}
}
If you don't know the formula you can use for example DotSpatial which provides transformations functions. (see here for an implementation example : https://github.com/xfischer/SqlServerSpatial.Toolkit/blob/4a60154f206af430b27de730afd0340db19f9191/SqlServerSpatial.Toolkit/Viewers/GDI/SqlGeometryReprojection.cs)

Related

Creating a class with implicit operators but it only works one way

A UDouble is an object that has units associated with the value (Value). The Unit is much more complex than shown here, but was converted to a string for the example.
public class UDouble
{
public string Unit { get; set; }
public double Value;
public UDouble(string unit)
{
Unit = unit;
}
public UDouble(string unit, double value)
{
Unit = unit;
Value = value;
}
public static implicit operator double(UDouble m)
{
return m.Value;
}
//public static implicit operator UDouble(double d)
//{
// return new UDouble("?????????", d); // wrong - units are lost
//}
}
static void Main()
{
UDouble p = new UDouble("mm", 2.3);
p.Value = 3;
double t = p;
p = 10.5;
}
The last line in the main() function (p = 10.5;) does not compile. What I what is the UDouble to act as if it is a double. In other words, I am trying to get p = 10.5; to work like p.Value = 10.5;
Is there a way to make it work?

OxyPlot: How to calculate in TrackerFormatString?

I try to create a line series where not only x- and y-values shall be displayed in the tracker, but one more property:
var series = new LineSeries {TrackerFormatString = "X:{2}\nY:{4}\nZ:{2*(1-4)}"};
series.Points.AddRange(...);
I want another property Z = (1-Y) * X to be displayed in the third row of the tracker. How is this possible?
It is possible by creating a custom
public class CustomDataPoint : IDataPointProvider
{
public double X { get; }
public double Y { get; }
public double Z{ get; }
public CustomDataPoint(double x, double y)
{
X = x;
Y = y;
Z = (1 - Y) * X;
}
public DataPoint GetDataPoint()
{
return new DataPoint(X, Y);
}
}
And then add it to the series:
var series = new LineSeries {TrackerFormatString = "X:{2}\nY:{4}\nZ:{Z}"};
series.ItemsSource = new [] {
new CustomDataPoint(...),
new CustomDataPoint(...)
};
Note that you need to use the ItemsSource property. You cannot simply add points with series.Point.AddRange(...).

Find blank space(rectangle) for signature field using PDFBox

When you want to create a visible signature using PDFBox you need to create a Rectangle2D object.
Rectangle2D humanRect = new Rectangle2D.Float(100, 200, 150, 50);
I would like to know if it is possible to find all the white spaces(rectangles) in the document(or from the first/last page) of a certain size (width x height).
I would like to choose one of these positions for my signature form.
I would like to use it as in the following example:
Rectangle2D humanRect = new Rectangle2D.Float(foundX, foundY, width, height);
As already confirmed in a comment to the question, you essentially are looking for a port of the functionality of the FreeSpaceFinder and FreeSpaceFinderExt classes for iText from this answer to PDFBox. This is the focus of this answer:
If you want to determine something from the content stream instructions of a page with PDFBox, you usually will create a class based on PDFStreamEngine or one of its subclasses. For anything that's not focusing on text extraction most often the PDFGraphicsStreamEngine is the base class of choice.
Based on that we can essentially copy the functionality of the mentioned iText based classes:
public class FreeSpaceFinder extends PDFGraphicsStreamEngine {
//
// constructors
//
public FreeSpaceFinder(PDPage page, float minWidth, float minHeight) {
this(page, page.getCropBox().toGeneralPath().getBounds2D(), minWidth, minHeight);
}
public FreeSpaceFinder(PDPage page, Rectangle2D initialBox, float minWidth, float minHeight) {
this(page, Collections.singleton(initialBox), minWidth, minHeight);
}
public FreeSpaceFinder(PDPage page, Collection<Rectangle2D> initialBoxes, float minWidth, float minHeight) {
super(page);
this.minWidth = minWidth;
this.minHeight = minHeight;
this.freeSpaces = initialBoxes;
}
//
// Result
//
public Collection<Rectangle2D> getFreeSpaces() {
return freeSpaces;
}
//
// Text
//
#Override
protected void showGlyph(Matrix textRenderingMatrix, PDFont font, int code, Vector displacement)
throws IOException {
super.showGlyph(textRenderingMatrix, font, code, displacement);
Shape shape = calculateGlyphBounds(textRenderingMatrix, font, code);
if (shape != null) {
Rectangle2D rect = shape.getBounds2D();
remove(rect);
}
}
/**
* Copy of <code>org.apache.pdfbox.examples.util.DrawPrintTextLocations.calculateGlyphBounds(Matrix, PDFont, int)</code>.
*/
private Shape calculateGlyphBounds(Matrix textRenderingMatrix, PDFont font, int code) throws IOException
{
GeneralPath path = null;
AffineTransform at = textRenderingMatrix.createAffineTransform();
at.concatenate(font.getFontMatrix().createAffineTransform());
if (font instanceof PDType3Font)
{
// It is difficult to calculate the real individual glyph bounds for type 3 fonts
// because these are not vector fonts, the content stream could contain almost anything
// that is found in page content streams.
PDType3Font t3Font = (PDType3Font) font;
PDType3CharProc charProc = t3Font.getCharProc(code);
if (charProc != null)
{
BoundingBox fontBBox = t3Font.getBoundingBox();
PDRectangle glyphBBox = charProc.getGlyphBBox();
if (glyphBBox != null)
{
// PDFBOX-3850: glyph bbox could be larger than the font bbox
glyphBBox.setLowerLeftX(Math.max(fontBBox.getLowerLeftX(), glyphBBox.getLowerLeftX()));
glyphBBox.setLowerLeftY(Math.max(fontBBox.getLowerLeftY(), glyphBBox.getLowerLeftY()));
glyphBBox.setUpperRightX(Math.min(fontBBox.getUpperRightX(), glyphBBox.getUpperRightX()));
glyphBBox.setUpperRightY(Math.min(fontBBox.getUpperRightY(), glyphBBox.getUpperRightY()));
path = glyphBBox.toGeneralPath();
}
}
}
else if (font instanceof PDVectorFont)
{
PDVectorFont vectorFont = (PDVectorFont) font;
path = vectorFont.getPath(code);
if (font instanceof PDTrueTypeFont)
{
PDTrueTypeFont ttFont = (PDTrueTypeFont) font;
int unitsPerEm = ttFont.getTrueTypeFont().getHeader().getUnitsPerEm();
at.scale(1000d / unitsPerEm, 1000d / unitsPerEm);
}
if (font instanceof PDType0Font)
{
PDType0Font t0font = (PDType0Font) font;
if (t0font.getDescendantFont() instanceof PDCIDFontType2)
{
int unitsPerEm = ((PDCIDFontType2) t0font.getDescendantFont()).getTrueTypeFont().getHeader().getUnitsPerEm();
at.scale(1000d / unitsPerEm, 1000d / unitsPerEm);
}
}
}
else if (font instanceof PDSimpleFont)
{
PDSimpleFont simpleFont = (PDSimpleFont) font;
// these two lines do not always work, e.g. for the TT fonts in file 032431.pdf
// which is why PDVectorFont is tried first.
String name = simpleFont.getEncoding().getName(code);
path = simpleFont.getPath(name);
}
else
{
// shouldn't happen, please open issue in JIRA
System.out.println("Unknown font class: " + font.getClass());
}
if (path == null)
{
return null;
}
return at.createTransformedShape(path.getBounds2D());
}
//
// Bitmaps
//
#Override
public void drawImage(PDImage pdImage) throws IOException {
Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
Rectangle2D unitSquare = new Rectangle2D.Float(0, 0, 1, 1);
Path2D path = new Path2D.Float(unitSquare);
path.transform(ctm.createAffineTransform());
remove(path.getBounds2D());
}
//
// Paths
//
#Override
public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException {
currentPath.moveTo(p0.getX(), p0.getY());
currentPath.lineTo(p1.getX(), p1.getY());
currentPath.lineTo(p2.getX(), p2.getY());
currentPath.lineTo(p3.getX(), p3.getY());
currentPath.closePath();
}
#Override
public void clip(int windingRule) throws IOException {
// ignore
}
#Override
public void moveTo(float x, float y) throws IOException {
currentPath.moveTo(x, y);
}
#Override
public void lineTo(float x, float y) throws IOException {
currentPath.lineTo(x, y);
}
#Override
public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException {
currentPath.curveTo(x1, y1, x2, y2, x3, y3);
}
#Override
public Point2D getCurrentPoint() throws IOException {
// To prevent many warnings...
return new Point2D.Float();
}
#Override
public void closePath() throws IOException {
currentPath.closePath();
}
#Override
public void endPath() throws IOException {
currentPath = new Path2D.Float();
}
#Override
public void strokePath() throws IOException {
// Better only remove the bounding boxes of the constituting strokes
remove(currentPath.getBounds2D());
currentPath = new Path2D.Float();
}
#Override
public void fillPath(int windingRule) throws IOException {
// Better only remove the bounding boxes of the constituting subpaths
remove(currentPath.getBounds2D());
currentPath = new Path2D.Float();
}
#Override
public void fillAndStrokePath(int windingRule) throws IOException {
// Better only remove the bounding boxes of the constituting subpaths
remove(currentPath.getBounds2D());
currentPath = new Path2D.Float();
}
#Override
public void shadingFill(COSName shadingName) throws IOException {
// ignore
}
//
// helpers
//
void remove(Rectangle2D usedSpace)
{
final double minX = usedSpace.getMinX();
final double maxX = usedSpace.getMaxX();
final double minY = usedSpace.getMinY();
final double maxY = usedSpace.getMaxY();
final Collection<Rectangle2D> newFreeSpaces = new ArrayList<Rectangle2D>();
for (Rectangle2D freeSpace: freeSpaces)
{
final Collection<Rectangle2D> newFragments = new ArrayList<Rectangle2D>();
if (freeSpace.intersectsLine(minX, minY, maxX, minY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), freeSpace.getWidth(), minY-freeSpace.getMinY()));
if (freeSpace.intersectsLine(minX, maxY, maxX, maxY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), maxY, freeSpace.getWidth(), freeSpace.getMaxY() - maxY));
if (freeSpace.intersectsLine(minX, minY, minX, maxY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), minX - freeSpace.getMinX(), freeSpace.getHeight()));
if (freeSpace.intersectsLine(maxX, minY, maxX, maxY))
newFragments.add(new Rectangle2D.Double(maxX, freeSpace.getMinY(), freeSpace.getMaxX() - maxX, freeSpace.getHeight()));
if (newFragments.isEmpty())
{
add(newFreeSpaces, freeSpace);
}
else
{
for (Rectangle2D fragment: newFragments)
{
if (fragment.getHeight() >= minHeight && fragment.getWidth() >= minWidth)
{
add(newFreeSpaces, fragment);
}
}
}
}
freeSpaces = newFreeSpaces;
}
void add(Collection<Rectangle2D> rectangles, Rectangle2D addition)
{
final Collection<Rectangle2D> toRemove = new ArrayList<Rectangle2D>();
boolean isContained = false;
for (Rectangle2D rectangle: rectangles)
{
if (rectangle.contains(addition))
{
isContained = true;
break;
}
if (addition.contains(rectangle))
toRemove.add(rectangle);
}
rectangles.removeAll(toRemove);
if (!isContained)
rectangles.add(addition);
}
//
// hidden members
//
Path2D currentPath = new Path2D.Float();
Collection<Rectangle2D> freeSpaces = null;
final float minWidth;
final float minHeight;
}
(FreeSpaceFinder)
Using this FreeSpaceFinder you can find empty areas with given minimum dimensions in a method like this:
public Collection<Rectangle2D> find(PDDocument pdDocument, PDPage pdPage, float minWidth, float minHeight) throws IOException {
FreeSpaceFinder finder = new FreeSpaceFinder(pdPage, minWidth, minHeight);
finder.processPage(pdPage);
return finder.getFreeSpaces();
}
(DetermineFreeSpaces method find)
Applied to the same PDF page as was the iText centric solution with minimum width 200 and height 50, we get:
Comparing to the analogous screen shot for the iText variant, we see that we get more possible rectangles here.
This is due to the iText solution using the font-level ascender and descender while we here use the individual glyph bounding boxes.

How to find all rectangles in a PDF using iText

A MS word document with a text box(rectangle) and I have successfully used libreoffice convert it to PDF.
How should I find all text box(rectangle) in pdf and How interpret the coordinates of a rectangle?
#Override
public void modifyPath(PathConstructionRenderInfo renderInfo) {
if (renderInfo.getOperation() == PathConstructionRenderInfo.RECT) {
float x = renderInfo.getSegmentData().get(0);
float y = renderInfo.getSegmentData().get(1);
float w = renderInfo.getSegmentData().get(2);
float h = renderInfo.getSegmentData().get(3);
Vector a = new Vector(x, y, 1).cross(renderInfo.getCtm());
Vector c = new Vector(x + w, y + h, 1).cross(renderInfo.getCtm());
implements ExtRenderListener, only allow find the page(A4) rectangle,do not find the (textbox)rectangle that contains all the content in a page.
As Bruno pointed out, the problem is that you may be faced with rectangles that are only defined by line-to or move-to operations.
You will need to keep track of all line-drawing operations, and 'aggregate' them as soon as they intersect (whenever a line is being drawn whos end/start matches up with an already known line's end/start).
public class RectangleFinder implements IEventListener {
private Map<Line, Integer> knownLines = new HashMap<>();
private Map<Integer, Integer> clusters = new HashMap<>();
public void eventOccurred(IEventData data, EventType type) {
if(data instanceof PathRenderInfo){
PathRenderInfo pathRenderInfo = (PathRenderInfo) data;
pathRenderInfo.preserveGraphicsState();
Path path = pathRenderInfo.getPath();
if(pathRenderInfo.getOperation() == PathRenderInfo.NO_OP)
return;
if(pathRenderInfo.getOperation() != PathRenderInfo.FILL)
return;
if(!isBlack(pathRenderInfo.getFillColor()))
return;
for(Subpath sPath : path.getSubpaths()){
for(IShape segment : sPath.getSegments()) {
if(segment instanceof Line) {
lineOccurred((Line) segment);
}
}
}
}
}
private boolean isBlack(Color c){
if(c instanceof IccBased){
IccBased col01 = (IccBased) c;
return col01.getNumberOfComponents() == 1 && col01.getColorValue()[0] == 0.0f;
}
if(c instanceof DeviceGray){
DeviceGray col02 = (DeviceGray) c;
return col02.getNumberOfComponents() == 1 && col02.getColorValue()[0] == 0.0f;
}
return false;
}
private void lineOccurred(Line line){
int ID = 0;
if(!knownLines.containsKey(line)) {
ID = knownLines.size();
knownLines.put(line, ID);
}else{
ID = knownLines.get(line);
}
Point start = line.getBasePoints().get(0);
Point end = line.getBasePoints().get(1);
for(Line line2 : knownLines.keySet()){
if(line.equals(line2))
continue;
if(line2.getBasePoints().get(0).equals(start)
|| line2.getBasePoints().get(1).equals(end)
|| line2.getBasePoints().get(0).equals(end)
|| line2.getBasePoints().get(1).equals(start)){
int ID2 = find(knownLines.get(line2));
clusters.put(ID, ID2);
break;
}
}
}
private int find(int ID){
int out = ID;
while(clusters.containsKey(out))
out = clusters.get(out);
return out;
}
public Set<EventType> getSupportedEvents() {
return null;
}
public Collection<Set<Line>> getClusters(){
Map<Integer, Set<Line>> out = new HashMap<>();
for(Integer val : clusters.values())
out.put(val, new HashSet<Line>());
out.put(-1, new HashSet<Line>());
for(Line l : knownLines.keySet()){
int clusterID = clusters.containsKey(knownLines.get(l)) ? clusters.get(knownLines.get(l)) : -1;
out.get(clusterID).add(l);
}
out.remove(-1);
return out.values();
}
public Collection<Rectangle> getBoundingBoxes(){
Set<Rectangle> rectangles = new HashSet<>();
for(Set<Line> cluster : getClusters()){
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
for(Line l : cluster){
for(Point p : l.getBasePoints()){
minX = Math.min(minX, p.x);
minY = Math.min(minY, p.y);
maxX = Math.max(maxX, p.x);
maxY = Math.max(maxY, p.y);
}
}
double w = (maxX - minX);
double h = (maxY - minY);
rectangles.add(new Rectangle((float) minX, (float) minY, (float) w, (float) h));
}
return rectangles;
}
}
This is a class I wrote to find black (filled) rectangles on a page.
With minor adjustments, it can find other rectangles as well.

How to subclass in Go

In C I can do something like this
struct Point {
int x,y;
}
struct Circle {
struct Point p; // must be first!
int rad;
}
void move(struct Point *p,int dx,int dy) {
....
}
struct Circle c = .....;
move( (struct Point*)&c,1,2);
Using this approach, I can pass any struct(Circle,Rectangle,etc) that has struct Point as first member.
How can I do the same in google go?
Actually, there's a simpler way to do it, which is more similar to the OP's example:
type Point struct {
x, y int
}
func (p *Point) Move(dx, dy int) {
p.x += dx
p.y += dy
}
type Circle struct {
*Point // embedding Point in Circle
rad int
}
// Circle now implicitly has the "Move" method
c := &Circle{&Point{0, 0}, 5}
c.Move(7, 3)
Also notice that Circle would also fulfill the Mover interface that PeterSO posted.
http://golang.org/doc/effective_go.html#embedding
Although Go has types and methods and
allows an object-oriented style of
programming, there is no type
hierarchy. The concept of “interface”
in Go provides a different approach
that we believe is easy to use and in
some ways more general. There are also
ways to embed types in other types to
provide something analogous—but not
identical—to subclassing. Is Go an
object-oriented language?, FAQ.
For example,
package main
import "fmt"
type Mover interface {
Move(x, y int)
}
type Point struct {
x, y int
}
type Circle struct {
point Point
rad int
}
func (c *Circle) Move(x, y int) {
c.point.x = x
c.point.y = y
}
type Square struct {
diagonal int
point Point
}
func (s *Square) Move(x, y int) {
s.point.x = x
s.point.y = y
}
func main() {
var m Mover
m = &Circle{point: Point{1, 2}}
m.Move(3, 4)
fmt.Println(m)
m = &Square{3, Point{1, 2}}
m.Move(4, 5)
fmt.Println(m)
}