How to get PROJ C++ to recognize grid directory? - c++

I've not been able to actually get EGM96 to WGS84 transformations to work in PROJ C++. I have however, gotten what I want to work in python, via pyproj, like this:
from pyproj import Transformer, CRS
from pyproj.transformer import TransformerGroup, TransformDirection
from pyproj.datadir import append_data_dir, get_data_dir
def main():
lat = 43.70012234
lng = -79.41629234
z = 100
append_data_dir("/absolute_directory_to/proj/")
transformer = Transformer.from_crs("epsg:4326", "epsg:5773",)
results = transformer.transform(lat, lng, z, None, False, True, TransformDirection.INVERSE)
print(results)
if __name__ == "__main__":
main()
This provides the result:
(43.70012234, -79.41629234, 62.71016021909354)
And the file us_nga_egm96_15.tif located in the ./proj/ directory
However, my replication of the same in C++ doesn't appear to work.
#include <proj.h>
#include <filesystem>
#include <array>
void main(){
auto proj_context = proj_context_create();
const char * path = "/absolute_directory_to/proj";
const char * db_path = proj_context_get_database_path(proj_context);
std::filesystem::path db_path_path = std::filesystem::path(db_path);
std::string db_path_str = db_path_path.parent_path().string();
std::array paths = {path, db_path_str.c_str()};
proj_context_set_search_paths(proj_context, paths.size(), paths.data());
std::cout << proj_errno_string(proj_context_errno(proj_context)) << std::endl;
auto temp = proj_create_crs_to_crs (proj_context,
"EPSG:4326",
"EPSG:5773",
NULL);
std::cout << proj_errno_string(proj_errno(temp)) << std::endl;
std::cout << proj_errno_string(proj_context_errno(proj_context)) << std::endl;
auto b = proj_trans(temp, PJ_INV, {43.70012234,-79.41629234,100,0});
std::cout << proj_errno_string(proj_errno(temp)) << std::endl;
std::cout << proj_errno_string(proj_context_errno(proj_context)) << std::endl;
std::cout << b.v[0] << "," << b.v[1] << "," << b.v[2] << "," << b.v[3] << std::endl;
std::cout << proj_errno_string(proj_errno(temp)) << std::endl;
std::cout << proj_errno_string(proj_context_errno(proj_context)) << std::endl;
proj_destroy(temp);
proj_context_destroy(proj_context);
return 0;
}
It actually prints out nothing (some strange character seems to be eating all the other characters), and in debug mode, I can see that b = {inf,inf,inf,inf}. Same thing happens if I don't manually specify the proj locaiton (but make sure the actual .tiff is located there).
What am I doing wrong here?

Something I didn't mention here, is that I was using VCPKG, due to https://github.com/microsoft/vcpkg/pull/16169, it is actually impossible to use GEO TIFF's on linux for PROJ through VCPKG I would recommend simply not using PROJ through VCPKG at all. The issues that are blocking this from being fixed have been blocked for years at this point, and don't seem to be something that is going to be fixed anytime soon.
seen how data is manually set here: https://raw.githubusercontent.com/Microsoft/vcpkg/master/ports/proj4/portfile.cmake
Stepping through the code, the issue was apparently that I set the correct file location, but that the code silently fails when TIFF_ENABLED is not defined.
//grids.cpp
if (IsTIFF(header_size, header)) {
#ifdef TIFF_ENABLED
auto set = std::unique_ptr<VerticalShiftGridSet>(
GTiffVGridShiftSet::open(ctx, std::move(fp), actualName));
if (!set)
pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
return set;
#else
pj_log(ctx, PJ_LOG_ERROR,
"TIFF grid, but TIFF support disabled in this build");
return nullptr;
#endif
Because I was using VCPKG, this was not possible to amend.

Related

Normalize slashes in path on Linux

What is the recommended way to specify to use native separators in filesystem path object?
Below is a simple example with a static string.
std::string str = "/tmp\\foo";
std::filesystem::path path(str);
std::cout << "Generic_string:" << path.generic_string() << std::endl;
std::cout << "Make_preferred:" << path.make_preferred() << std::endl;
This will output
Generic_string:/tmp\foo
Make_preferred:/tmp\foo
The make_preferred method does not appear to make any change to path. Do I really need to use std::replace to fix the issue?

Replacement for pj_get_def?

I have an application that parses the output of pj_get_def that turns EPSG identifiers into some useful key/value pairs.
According to the migration guide, the replacement is proj_pj_info. However, the PJ_PROJ_INFO I get back has an empty definition and the description is an ambiguous (if human readable) string.
Here's test code:
#include <proj.h>
#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
#include <proj_api.h>
#include <iostream>
#include <string>
void test_pj(int crs)
{
auto const arg = "+init=epsg:" + std::to_string(crs);
auto* const proj = pj_init_plus(arg.c_str());
auto* const text = pj_get_def(proj, 0);
std::cout << "PROJ 4\n";
std::cout << "text: '" << text << "'\n";
}
void test_proj(int crs)
{
auto const arg = "EPSG:" + std::to_string(crs);
auto* const proj = proj_create(nullptr, arg.c_str());
auto const& info = proj_pj_info(proj);
std::cout << "PROJ 5\n";
// std::cout << "id: '" << info.id << "'\n"; crash?
std::cout << "definition: '" << info.definition << "'\n";
std::cout << "description: '" << info.description << "'\n";
}
int main()
{
test_pj(4326);
test_proj(4326);
}
And here's the output:
PROJ 4
text: ' +init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0'
PROJ 5
definition: ''
description: 'WGS 84'
What is the replacement for pj_get_def? How do I decompose a PJ into the interesting information I could get in PROJ 4?
Well, the documentation is just wrong. The correct replacement isn't proj_pj_info, it's proj_as_proj_string.
There's a small caveat, however: in PROJ 4, this included the ellipsoid. In PROJ 6, it does not. If the ellipsoid is needed, an additional call to proj_get_ellipsoid (followed by another proj_as_proj_string) is required.
Edit: So, it turns out proj_pj_info does work... but only in PROJ 5 (not PROJ 6), and only using +init=epsg:N (which is not supported in PROJ 6).
I didn't dig very deeply, but I suspect that PROJ 5 doesn't support the EPSG:N syntax at all, making that a hard compatibility break between 5 and 6 with, apparently, no way to write code that works on both versions without resorting to version-conditional logic.

Tesseract - getting alternative results with confidence level

I'm trying to use tesseract to recognize numbers. It works pretty well but sometimes I get wrong results (8 instead of 9 etc). I know how to improve accuracy with some non-ocr methods but I need other results with confidence levels. I found out that I get the best choices using a ChoiceIterator for each ResultIterator:
tesseract::TessBaseAPI tess;
tess.SetPageSegMode(tesseract::PSM_SINGLE_CHAR);
tess.SetVariable("save_best_choices", "T");
[setting input image here]
tess.Recognize(NULL);
tesseract::ResultIterator* ri = tess.GetIterator();
tesseract::ChoiceIterator* ci;
if(ri != 0)
{
char* symbol = ri->GetUTF8Text(tesseract::RIL_SYMBOL);
if(symbol != 0)
{
float conf = ri->Confidence(tesseract::RIL_SYMBOL);
cout << symbol << "\tconf: " << conf << "\n";
const tesseract::ResultIterator itr = *ri;
ci = new tesseract::ChoiceIterator(itr);
do {
const char* choice = ci->GetUTF8Text();
if (choice) {
std::cout << "\t" << choice << " conf: " << ci->Confidence() << "\n";
}
} while(ci->Next());
// delete[] symbol;
}
}
But the problem is that ci->GetUTF8Text() always returns a null pointer.
I'm using tesseract 3.02 as DLL, compiled under MSVC2010 express.
From limited research earlier today, I think the answer might be setting the variable SaveBlobChoices versus SaveBestChoices. I still need to test this myself.

Has anyone been able to use libsensors properly?

Long story short I am trying to write an application that can check cpu temperatures. Using the libsensors(3) man pages I've been able to at least get the libsensors_version number. As of now, here is my code:
#include <sensors/sensors.h>
#include "SensorData.h"
#include <string>
#include <sstream>
using namespace std;
SensorData::SensorData()
{
sensors_init(NULL);
}
SensorData::~SensorData()
{
sensors_cleanup();
}
string SensorData::GetVersion()
{
ostringstream Converter;
Converter<<"Version: "<<libsensors_version;
return Converter.str();
}
void SensorData::FetchTemp()
{
//sensors_get_value()
}
With the man pages I know that sensors_get_value expects
const sensors_chip_name *name
int subfeat_nr
double *value
to be passed to it. The problem is I have no idea what those are exactly. Just about every function in the documentation has this problem. They all expect vague things I don't know how to supply.
So here is the bulk of the question: Does anyone have any working examples of this library I could look at? Or at the very least does anyone know how to give these functions the values they need?
EDIT:
Since no one seems to know much about this library, does anyone know of a different way to get temperatures?
You can find out how to use the API by browsing the source code. The code for the sensors program isn't too complex to follow.
To get you started, here's a quick function that:
enumerates all the chips
enumerates all their features
prints the values of their readable subfeatures
You can just add it to your existing skeleton class as-is.
(This code is for demo purposes only, not tested thoroughly at all.)
void SensorData::FetchTemp()
{
sensors_chip_name const * cn;
int c = 0;
while ((cn = sensors_get_detected_chips(0, &c)) != 0) {
std::cout << "Chip: " << cn->prefix << "/" << cn->path << std::endl;
sensors_feature const *feat;
int f = 0;
while ((feat = sensors_get_features(cn, &f)) != 0) {
std::cout << f << ": " << feat->name << std::endl;
sensors_subfeature const *subf;
int s = 0;
while ((subf = sensors_get_all_subfeatures(cn, feat, &s)) != 0) {
std::cout << f << ":" << s << ":" << subf->name
<< "/" << subf->number << " = ";
double val;
if (subf->flags & SENSORS_MODE_R) {
int rc = sensors_get_value(cn, subf->number, &val);
if (rc < 0) {
std::cout << "err: " << rc;
} else {
std::cout << val;
}
}
std::cout << std::endl;
}
}
}
}
The Gnome panel Sensors applet works with libsensors (and other backends); the full sources are available from Sourceforge, here: http://sensors-applet.sourceforge.net/index.php?content=source
… in particular, the libsensors plug-in looks fairly legible… I believe this should be a usable gitweb link straight to that code: http://sensors-applet.git.sourceforge.net/git/gitweb.cgi?p=sensors-applet/sensors-applet;a=blob;f=plugins/libsensors/libsensors-plugin.c;h=960c19f4c36902dee4e20b690f2e3dfe6c715279;hb=HEAD
Your code should looks like this:
/* Read /etc/sensors.d to get the names or use code in above post */
std::string chip_name = "CHIP_NAME-*";
/* Here you get the path to the chip you want to read */
int rc;
sensors_chip_name name;
rc = sensors_parse_chip_name(chip_name.c_str(), &name);
/* Check rc != 0 */
/* Here you get the internal structure */
int nr = 0; //Here I silently assume you have only one chip to read
const sensors_chip_name* p_chip;
p_chip = sensors_get_detected_chips(&name, &nr);
/* Check p_chip != 0 */
/* Now you read the value - this you can repeat in some for/while cycle */
double val;
/* Replace the "1" with the feature you want to read */
rc = sensors_get_value(p_chip, 1, &val);
std::cout << "Now I can use sensors library " << val << std::endl;
Hope it helps despite the fact it is not copy/paste solution.
You can obtain the const sensors_chip_name* p_chip; from the code above post as well.
I believe the problem is in fact the const sensors_chip_name MUST be returned and filled by sensors library.

TOUGH: Dealing with deeply nested pointers in C++

I define this structure:
struct s_molecule
{
std::string res_name;
std::vector<t_particle> my_particles;
std::vector<t_bond> my_bonds;
std::vector<t_angle> my_angles;
std::vector<t_dihedral> my_dihedrals;
s_molecule& operator=(const s_molecule &to_assign)
{
res_name = to_assign.res_name;
my_particles = to_assign.my_particles;
my_bonds = to_assign.my_bonds;
my_angles = to_assign.my_angles;
my_dihedrals = to_assign.my_dihedrals;
return *this;
}
};
and these structures:
typedef struct s_particle
{
t_coordinates position;
double charge;
double mass;
std::string name;
std::vector<t_lj_param>::iterator my_particle_kind_iter;
s_particle& operator=(const s_particle &to_assign)
{
position = to_assign.position;
charge = to_assign.charge;
mass = to_assign.mass;
name = to_assign.name;
my_particle_kind_iter = to_assign.my_particle_kind_iter;
return *this;
}
} t_particle;
struct s_bond
{
t_particle * particle_1;
t_particle * particle_2;
std::vector<t_bond_param>::iterator my_bond_kind_iter;
s_bond& operator=(const s_bond &to_assign)
{
particle_1 = to_assign.particle_1;
particle_2 = to_assign.particle_2;
my_bond_kind_iter = to_assign.my_bond_kind_iter;
return *this;
}
};
and then in my code I return a pointer to an s_molecule (typedef'd to t_molecule, but still).
Using this pointer I can get this code to work:
for (unsigned int i = 0;
i < current_molecule->my_particles.size();
i++)
{
std::cout << "Particle "
<< current_molecule->my_particles[i].name << std::endl
<< "Charge: "
<< current_molecule->my_particles[i].charge << std::endl
<< "Mass: "
<< current_molecule->my_particles[i].mass << std::endl
<< "Particle Kind Name: "
<< (*current_molecule->my_particles[i].my_particle_kind_iter).atom_kind_name
<< std::endl
<< "x: " << current_molecule->my_particles[i].position.x
<< " y: " << current_molecule->my_particles[i].position.y
#ifdef USE_3D_GEOM
<< "z: " << current_molecule->my_particles[i].position.z
#endif
<< std::endl;
}
If I replace it with:
for (std::vector<t_particle>::iterator it = current_molecule->my_particles.begin();
it !=current_molecule->my_particles.end();
it++)
{
std::cout << "Particle "
<< (*it).name << std::endl
<< "Charge: "
<< (*it).charge << std::endl
<< "Mass: "
<< (*it).mass << std::endl
<< "Particle Kind Name: "
<< (*(*it).my_particle_kind_iter).atom_kind_name
<< std::endl
<< "x: " << (*it).position.x
<< " y: " << (*it).position.y
#ifdef USE_3D_GEOM
<< "z: " << (*it).position.z
#endif
<< std::endl;
}
I now get nasty segfaults...
Not to put too much here, but I'm also getting segfaults when I tried to do this:
std::cout << "Bond ATOMS : "
<< (*current_molecule).my_bonds[0].particle_1->name
<< std::endl
Again, current_molecule is a pointer to a s_molecule structure, which contains arrays of structures, which in turn either directly have vars or are pointers. I can't get these multiple layers of indirection to work. Suggestions on fixing these segfaults.
FYI I'm compiling on Linux Centos 5.4 with g++ and using a custom makefile system.
#sbi Thanks for the good advice! I believe you are right -- the assignment overloaded operator is unnecessary and should be scrapped.
I've followed the approach of commenting out stuff and am very confused. Basically in the function that passes the pointer to my particular molecule to the main function to print, I can see all the data in that molecule (bonds, particles, name, etc) perfectly, printing with cout's.
Once I pass it to the main as a ptr, if I use that ptr with an iterator I get a segfault. In other words. Also for some reason the bond data (which I can freely print in my funct that returns to the pointer) also segfaults if I try to print it, even if I use the [] to index the vector of bonds (which works for the particle vector).
That's the best info I can give for now.
A wild guess: Are you using shared libraries. I remember having difficulties passing STL-containers back and forth across shared library boundaries.
Jason (OP) was asked in a comment by David Rodríguez:
Are you returning a pointer to a local variable?
Jason answered:
No its a ptr to a class variable. The class is very much in existence (it contains the function that returns the molecule).
Unless you're talking of a true class variable (qualified as static), the fact that the class exists doesn't have much to do with it. Instances of a class exist, and they might have ceased to exist even if you just called a function on them.
As such, the question is:
Does the instance of the class that returned the pointer current_molecule still exist?
Or is current_molecule qualified as static, i.e. being a true class variable?
If the answer to both questions is "no", you're in Undefined County.
At this point, it becomes very important that you post source code that can be used by us here to actually reproduce the problem; it might well be located in source you aren't showing us.
Again, this issue was answered here:
Weird Pointer issue in C++
by DeadMG. Sorry for the double post.