Serialize data from PostGIS to JSON - serialization

I'm working on a .NET Core REST service which will have a controller that will send geometry data back a GeoJson so I can show it on my Leaflet map.
I'm having trouble converting my class to JSON.
I've tried this:
StringBuilder sb = new StringBuilder();
JsonTextWriter writer = new JsonTextWriter(new StringWriter(sb));
var jsonSerializer = GeoJsonSerializer.Create(new JsonSerializerSettings()
{NullValueHandling = NullValueHandling.Ignore});
jsonSerializer.Serialize(writer, _context.Locations);
writer.Flush();
return Ok(sb.ToString());
But then I get this error
Self referencing loop detected for property 'CoordinateValue' with type 'GeoAPI.Geometries.Coordinate'
I've been looking for examples and reading docs all day but can't get it to work.
The class I need to serialize:
public class SobekLocation
{
public int Id { get; set; }
public string LocationId { get; set; }
public bool Use { get; set; }
public Point Location { get; set; }
}
Any suggestions are much appreciated.
Edit:
I'm making my own featurecollection and am trying to serialize that. It is doing something but it is not valid GeoJSON.
public FeatureCollection GetLocationsAsFeatureCollection()
{
var featureCollection = new FeatureCollection();
foreach (var location in GetLocations().Take(5))
{
var attr = new AttributesTable
{
{"Id", location.Id},
{"Use", location.Use},
{"LocationId", location.LocationId}
};
var feature = new Feature(location.Location, attr);
featureCollection.Add(feature);
}
return featureCollection;
}
And de serialization part:
var fc = _service.GetLocationsAsFeatureCollection();
var sb = new StringBuilder();
var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings
{
// To prevent the Self referencing loop error:
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
using (var sw = new StringWriter(sb))
{
jsonSerializer.Serialize(sw, fc);
}
return Ok(sb.ToString());
The serialized data:
{
"Features":[
{
"Geometry":{
"CoordinateSequence":{
"Dimension":3,
"Ordinates":7,
"Count":1
},
"Coordinates":[
{
"X":191390.015,
"Y":523064.716597462,
"Z":"NaN"
}
],
"NumPoints":1,
"IsEmpty":false,
"Dimension":0,
"BoundaryDimension":-1,
"X":191390.015,
"Y":523064.716597462,
"Coordinate":{
"X":191390.015,
"Y":523064.716597462,
"Z":"NaN"
},
"GeometryType":"Point",
"OgcGeometryType":1,
"Boundary":[
],
"Z":"NaN",
"M":"NaN",
"Factory":{
"PrecisionModel":{
"IsFloating":true,
"MaximumSignificantDigits":16,
"Scale":0,
"PrecisionModelType":0,
"OffsetX":0,
"OffsetY":0
},
"CoordinateSequenceFactory":{
"Ordinates":7
},
"SRID":28992
},
"UserData":null,
"SRID":28992,
"PrecisionModel":{
"IsFloating":true,
"MaximumSignificantDigits":16,
"Scale":0,
"PrecisionModelType":0,
"OffsetX":0,
"OffsetY":0
},
"NumGeometries":1,
"IsSimple":true,
"IsValid":true,
"Area":0,
"Length":0,
"EnvelopeInternal":{
"IsNull":false,
"Width":0,
"Height":0,
"MinX":191390.015,
"MaxX":191390.015,
"MinY":523064.716597462,
"MaxY":523064.716597462,
"Area":0,
"MinExtent":0,
"MaxExtent":0,
"Centre":{
"X":191390.015,
"Y":523064.716597462,
"Z":"NaN"
}
},
"IsRectangle":false
},
"Attributes":[
{
"Key":"Id",
"Value":1
},
{
"Key":"Use",
"Value":false
},
{
"Key":"LocationId",
"Value":"KNP_1"
}
],
"BoundingBox":null
}
],
"Type":"FeatureCollection",
"CRS":null,
"Count":5,
"BoundingBox":null
}
As you can see all properties start with an uppercase but they should be in lowercase. And after changing the properties manually geojsonlint.com reports: GeoJSON features must have a type=feature member
Edit2:
I've made some progress. I'm using the GeoJSON.Net package now and I can now create valid GeoJSON accordin to http://geojsonlint.com/. But still my Leaflet map is complaining about invalid GeoJSON so the map is still not showing anything ;)
Here's my new converter:
public GeoJSON.Net.Feature.FeatureCollection GetLocationsAsGeoJsonFeatureCollection()
{
var featureCollection = new GeoJSON.Net.Feature.FeatureCollection();
foreach (var location in GetLocations())
{
var attr = new Dictionary<string, object>()
{
{"Id", location.Id},
{"Use", location.Use},
{"LocationId", location.LocationId}
};
var position = new Position(location.Location.Coordinate.X, location.Location.Coordinate.Y);
var geom = new GeoJSON.Net.Geometry.Point(position);
var feature = new GeoJSON.Net.Feature.Feature(geom, attr, location.Id.ToString());
featureCollection.Features.Add(feature);
}
return featureCollection;
}
And the GeoJSON it produces:
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"id":"1",
"geometry":{
"type":"Point",
"coordinates":[
523064.716597462,
191390.015
]
},
"properties":{
"id":1,
"use":false,
"locationId":"KNP_1"
}
},
{
"type":"Feature",
"id":"2",
"geometry":{
"type":"Point",
"coordinates":[
519349.860113963,
170162.249352578
]
},
"properties":{
"id":2,
"use":false,
"locationId":"CP_6"
}
},
{
"type":"Feature",
"id":"3",
"geometry":{
"type":"Point",
"coordinates":[
519952.507022603,
170402.383673312
]
},
"properties":{
"id":3,
"use":false,
"locationId":"CP_UR_5_1"
}
},
{
"type":"Feature",
"id":"4",
"geometry":{
"type":"Point",
"coordinates":[
519948.062527202,
170582.612655391
]
},
"properties":{
"id":4,
"use":false,
"locationId":"CP_UR_6_1"
}
},
{
"type":"Feature",
"id":"5",
"geometry":{
"type":"Point",
"coordinates":[
519902.432252114,
170602.894875503
]
},
"properties":{
"id":5,
"use":false,
"locationId":"CP_UR_6_2"
}
}
]
}
This is my code for Leaflet:
L.geoJson('http://localhost:5000/api/data/',
{
attribution: 'Mine'
}
).addTo(map);
I'm still looking for advice how to easily plot my PostGIS data on a Leaflet map.

Related

Shopify Storefront API getProductMedia

I tried following the offical Shopify Documentation for retrieving ProductMedia.
My Query looks like this:
query getProductMediaById($id: ID!) {
product(id: $id) {
id
media(first: 10) {
edges {
node {
mediaContentType
alt
...mediaFieldsByType
}
}
}
}
}
fragment mediaFieldsByType on Media {
... on ExternalVideo {
id
embeddedUrl
}
... on MediaImage {
image {
...imageAttributes
}
}
... on Model3d {
sources {
url
mimeType
format
filesize
}
}
... on Video {
sources {
url
mimeType
format
height
width
}
}
}
fragment imageAttributes on Image {
altText
url
}
The only thing where I diverged from the official documentation is to put the image attributes to another fragment for code reuse.
But when I try to execute the query I get the following response:
{
"data": {
"product": {
"__typename": "Product",
"id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzY3NjcyOTczMzEzMDU=",
"media": {
"__typename": "MediaConnection",
"edges": [
{
"__typename": "MediaEdge",
"node": {
"__typename": "MediaImage",
"mediaContentType": "IMAGE",
"alt": ""
}
}
]
}
}
},
"loading": false,
"networkStatus": 7
}
Or to put it to words my response doesn't contain any information from the mediaFieldsByType fragment.
Any Idea what I'm doing wrong?

Axios call in recursive function - Vue.js

I'm new to vue.js. In my code, I try to recurse JSON schema and get all the nested properties in a single object. In some cases, fetch properties from API calls. kindly guide.
JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "UNDP",
"description": "UNDP PRISM Data Capturing Form",
"type": "object",
"x-display": "form",
"properties": {
"concepts": {
"title": "Concepts",
"type": "object",
"properties": {
"indicators": {
"$ref": "/Form/GetIndicators"
}
}
}
}
}
My function
parseJsonToObject(schema:any) {
if (!schema) { return }
if(schema.$ref) {
let params = {
id: schema.id
};
let response = $axios.get(schema.$ref, params);
schema.properties = response.data.properties;
}
if (schema.type === 'string' || schema.type === 'number') {
return schema.title;
}
const parsedData = {};
Object.keys(schema.properties || {}).forEach( (item) => {
parsedData[item] = this.parseDomainSchemaToObject(schema.properties[item])
})
return parsedData
}
I got the result
{
"concepts": {
"indicators": {}
}
}
I have also tried async/await
async parseToObject(schema:any) {
let response = await this.getRefPropertiesFormServer(schema.$ref, params) ;
// code
}
async getRefPropertiesFormServer(url, params){
return $axios.get(url, params);
}
then I got below result
{
"concepts": {
"indicators": new Promise()
}
}

Convert File in MS Graph API on SPFx return undefined

When i try to download a file from API Graph accesing to Drive or Sites with javascript on SPFx this return undefined.
my webpart code:
import { Version } from '#microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '#microsoft/sp-webpart-base';
import * as strings from 'Docx2PdfWebPartStrings';
import { MSGraphClient } from '#microsoft/sp-http';
export interface IDocx2PdfWebPartProps {
description: string;
}
export default class Docx2PdfWebPart extends BaseClientSideWebPart<IDocx2PdfWebPartProps> {
public async render(): Promise<void> {
const client: MSGraphClient = await this.context.msGraphClientFactory.getClient();
var tenant = 'test';
var siteID = `${tenant}.sharepoint.com,12adb250-26f4-4dbb-9545-71d029bad763,8fdc3f56-2d6d-42d9-9a4d-d684e73c341e`;
var fileID = '01MBNFB7EIQLARTATNE5G3XDJNYBD2A3IL';
var fileName = 'Test.docx';
//This work
var site = await client.api(`/sites/${tenant}.sharepoint.com:/sites/dev:/drive?$select=id,weburl`).get();
console.log(site);
try {
//This not work
var fileFromDrive = await client.api(`/drive/root:/${fileName}:/content?format=pdf`).get();
console.log(fileFromDrive);
var fileFromSite = await client.api(`/sites/${siteID}/drive/items/${fileID}/content?format=pdf`).get();
console.log(fileFromSite);
} catch (error) {
console.log(error);
}
this.domElement.innerHTML = `<h1>Hola Mundo</h1>`;
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}
The chrome console log
But when i use Graph Explorer it works correctly
This is my package-solution.json
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "docx-2-pdf-client-side-solution",
"id": "f4b5db4f-d9ff-463e-b62e-0cc9c9e94089",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"webApiPermissionRequests": [
{
"resource": "Microsoft Graph",
"scope": "Sites.Read.All"
},
{
"resource": "Microsoft Graph",
"scope": "Files.Read.All"
},
{
"resource": "Microsoft Graph",
"scope": "Files.ReadWrite.All"
},
{
"resource": "Microsoft Graph",
"scope": "Sites.ReadWrite.All"
}
]
},
"paths": {
"zippedPackage": "solution/docx-2-pdf.sppkg"
}
}
I use the following articles
https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest-1.0&tabs=javascript
https://learn.microsoft.com/en-us/graph/api/driveitem-get-content-format?view=graph-rest-1.0&tabs=javascript#code-try-1
Try using the callback property instead of await:
client.api(`/drive/root:/${fileName}:/content?format=pdf`).get((err, response) => console.log("your response:", err, response));

BigQueryIO.write DynamicDestination withCreateDisposition - Clustering fields

BigQueryIO.write.withCreateDisposition(CreateDisposition.CREATE_IF_NEEDED) along with DynamicDestinations we can write to the dynamic table and if the table does not exist it will create the table from TableSchema provided from DynamicDestinations.
I am not able to add clustering fields part of TableSchema model, because it does not have such a feature.
how we can add DynamicDestinations having TableSchema with clustering fields?
bigQuery API is one way to add cluster field to a table
Using this link you can test the API before writing your code
function execute() {
return gapi.client.bigquery.jobs.insert({
"resource": {
"configuration": {
"query": {
"clustering": {
"fields": [
"Field1",
"Field2"
]
},
"query": "select 5",
"destinationTable": {
"datasetId": "Id1",
"projectId": "Project1",
"tableId": "T1"
}
}
}
}
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error", err); });
}
And This is a JS example on how to manipulate the parameters:
static setConfiguration(params, configuration) {
//To have a destination table we MUST have a tableId
if (params.destinationTable && params.destinationTable.tableId) {
configuration.query.destinationTable = params.destinationTable
}
if (params.clusteringFields) {
configuration.query.clustering = {fields: params.clusteringFields}
}
if (params.timePartitioning) {
configuration.query.timePartitioning = {
type: 'DAY',
field: params.timePartitioning
}
}
if (params.writeDisposition) {
configuration.query.writeDisposition = params.writeDisposition
}
if (params.queryPriority && params.queryPriority.toUpperCase() === "BATCH") {
configuration.query.priority = "BATCH"
}
if (params.useCache === false) {
configuration.query.useQueryCache = params.useCache
}
if (params.maxBillBytes) {
configuration.query.maximumBytesBilled = params.maxBillBytes
}
if (params.maxBillTier) {
configuration.query.maximumBillingTier = params.maxBillTier
}
}
Now after version 2.16.0, BigQueryIO does provide an option to add clusteringFields in dynamic destinations.
#Override
public TableDestination getTable(String eventName) {
return new TableDestination(tableSpec,
tableDescription, timePartitioning, clustering);
}
Notice that the 4th parameter is clustering, which you can use.

Elastic Search when to add dynamic mappings

I've been having troubles with Elastic Search (ES) dynamic mappings. Seems like I'm in a catch-22. https://www.elastic.co/guide/en/elasticsearch/guide/current/custom-dynamic-mapping.html
The main goal is to store everything as a string that comes into ES.
What I've tried:
In ES you can't create a dynamic mapping until the index has been
created. Okay, makes sense.
I can't create an empty index, so if
the first item sent into the index is not a string, I can't
re-assign it... I won't know what type of object with be the first
item in the index, it could be any type, due to how the the app accepts a variety of objects/events.
So if I can't create the mapping ahead of time, and I can't insert an empty index to create the mapping, and I can't change the mapping after the fact, how do I deal with the first item if its NOT a string???
Here's what I'm currently doing (using the Javascript Client).
createESIndex = function (esClient){
esClient.index({
index: 'timeline-2015-11-21',
type: 'event',
body: event
},function (error, response) {
if (error) {
logger.log(logger.SEVERITY.ERROR, 'acceptEvent elasticsearch create failed with: '+ error + " req:" + JSON.stringify(event));
console.log(logger.SEVERITY.ERROR, 'acceptEvent elasticsearch create failed with: '+ error + " req:" + JSON.stringify(event));
res.status(500).send('Error saving document');
} else {
res.status(200).send('Accepted');
}
});
}
esClientLookup.getClient( function(esClient) {
esClient.indices.putTemplate({
name: "timeline-mapping-template",
body:{
"template": "timeline-*",
"mappings": {
"event": {
"dynamic_templates": [
{ "timestamp-only": {
"match": "#timestamp",
"match_mapping_type": "date",
"mapping": {
"type": "date",
}
}},
{ "all-others": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"type": "string",
}
}
}
]
}
}
}
}).then(function(res){
console.log("put template response: " + JSON.stringify(res));
createESIndex(esClient);
}, function(error){
console.log(error);
res.status(500).send('Error saving document');
});
});
Index templates to the rescue !! That's exactly what you need, the idea is to create a template of your index and as soon as you wish to store a document in that index, ES will create it for you with the mapping you gave (even dynamic ones)
curl -XPUT localhost:9200/_template/my_template -d '{
"template": "index_name_*",
"settings": {
"number_of_shards": 1
},
"mappings": {
"type_name": {
"dynamic_templates": [
{
"strings": {
"match": "*",
"match_mapping_type": "*",
"mapping": {
"type": "string"
}
}
}
],
"properties": {}
}
}
}'
Then when you index anything in an index whose name matches index_name_*, the index will be created with the dynamic mapping above.
For instance:
curl -XPUT localhost:9200/index_name_1/type_name/1 -d '{
"one": 1,
"two": "two",
"three": true
}'
That will create a new index called index_name_1 with a mapping type for type_name where all properties are string. You can verify that with
curl -XGET localhost:9200/index_name_1/_mapping/type_name
Response:
{
"index_name_1" : {
"mappings" : {
"type_name" : {
"dynamic_templates" : [ {
"strings" : {
"mapping" : {
"type" : "string"
},
"match" : "*",
"match_mapping_type" : "*"
}
} ],
"properties" : {
"one" : {
"type" : "string"
},
"three" : {
"type" : "string"
},
"two" : {
"type" : "string"
}
}
}
}
}
}
Note that if you're willing to do this via the Javascript API, you can use the indices.putTemplate call.
export const user = {
email: {
type: 'text',
},
};
export const activity = {
date: {
type: 'text',
},
};
export const common = {
name: {
type: 'text',
},
};
import { Client } from '#elastic/elasticsearch';
import { user } from './user';
import { activity } from './activity';
import { common } from './common';
export class UserDataFactory {
private schema = {
...user,
...activity,
...common,
relation_type: {
type: 'join',
eager_global_ordinals: true,
relations: {
parent: ['activity'],
},
},
};
constructor(private client: Client) {
Object.setPrototypeOf(this, UserDataFactory.prototype);
}
async create() {
const settings = {
settings: {
analysis: {
normalizer: {
useLowercase: {
filter: ['lowercase'],
},
},
},
},
mappings: {
properties: this.schema,
},
};
const { body } = await this.client.indices.exists({
index: ElasticIndex.UserDataFactory,
});
await Promise.all([
await (async (client) => {
await new Promise(async function (resolve, reject) {
if (!body) {
await client.indices.create({
index: ElasticIndex.UserDataFactory,
});
}
resolve({ body });
});
})(this.client),
]);
await this.client.indices.close({ index: ElasticIndex.UserDataFactory });
await this.client.indices.putSettings({
index: ElasticIndex.UserDataFactory,
body: settings,
});
await this.client.indices.open({
index: ElasticIndex.UserDataFactory,
});
await this.client.indices.putMapping({
index: ElasticIndex.UserDataFactory,
body: {
dynamic: 'strict',
properties: {
...this.schema,
},
},
});
}
}
wrapper.ts
class ElasticWrapper {
private _client: Client = new Client({
node: process.env.elasticsearch_node,
auth: {
username: 'elastic',
password: process.env.elasticsearch_password || 'changeme',
},
ssl: {
ca: process.env.elasticsearch_certificate,
rejectUnauthorized: false,
},
});
get client() {
return this._client;
}
}
export const elasticWrapper = new ElasticWrapper();
index.ts
new UserDataFactory(elasticWrapper.client).create();