Shape is not getting plotted on the map image generated - geojson

I am trying to create map image from the attached JSON the image shows correct location but does not plot polygon.
This is happening only in case of one feature having circle shape whereas for other shapes they are rendered fine.
Following function is used to create image from json.
public static Image CreateMapImage(string json,
KnownTileSource baseMapTile = KnownTileSource.BingRoads)
{
double maxX = Double.MinValue,
maxY = Double.MinValue,
minX = Double.MaxValue,
minY = Double.MaxValue;
var map = new SharpMap.Map(new Size(800, 400));
map.Layers.Add(new TileLayer(
KnownTileSources.Create(
baseMapTile, bingKey), "BingRoad"));
JObject rss = JObject.Parse(json);
foreach (JObject shape in rss["features"])
{
var jsonReader = new NetTopologySuite.IO.GeoJsonReader();
var geom = jsonReader.Read<IGeometry>(shape.ToString(Formatting.None));
var fp = new GeometryFeatureProvider(geom);
var layer = new VectorLayer("geojson", fp);
layer.CoordinateTransformation = new
CoordinateTransformationFactory().CreateFromCoordinateSystems(
GeographicCoordinateSystem.WGS84,
ProjectedCoordinateSystem.WebMercator);
layer.Style = new SharpMap.Styles.VectorStyle()
{
Fill = new SolidBrush(Color.FromArgb(100, 255, 0, 0)),
Outline = new Pen(Color.Red, 1.5f),
EnableOutline = true
};
maxX = layer.Envelope.MaxX > maxX ? layer.Envelope.MaxX : maxX;
maxY = layer.Envelope.MaxY > maxY ? layer.Envelope.MaxY : maxY;
minX = layer.Envelope.MinX < minX ? layer.Envelope.MinX : minX;
minY = layer.Envelope.MinY < minY ? layer.Envelope.MinY : minY;
map.Layers.Add(layer);
}
map.ZoomToBox(new Envelope(new GeoPoint(m
inX, minY), new GeoPoint(maxX, maxY)));
map.Zoom *= 1.1;
return map.GetMap();
}
I am using following Geojson file to render the shape
{
"features": [{
"id": "4",
"geometry": {
"coordinates": [[[73.879909, 18.521356], [73.879617, 18.526641], [73.878744, 18.531869], [73.877299, 18.53698],
[73.875299, 18.541921], [73.872765, 18.546636], [73.869725, 18.551074], [73.866212, 18.555186], [73.862265, 18.558928],
[73.857928, 18.562258], [73.853246, 18.56514], [73.848273, 18.567542], [73.843062, 18.569438], [73.837671, 18.570808],
[73.832158, 18.571635], [73.826584, 18.571912], [73.82101, 18.571635], [73.815497, 18.570808], [73.810105, 18.569438],
[73.804894, 18.567542], [73.799921, 18.56514], [73.79524, 18.562258], [73.790902, 18.558928], [73.786955, 18.555186],
[73.783442, 18.551074], [73.780402, 18.546636], [73.777868, 18.541921], [73.775868, 18.53698], [73.774423, 18.531869],
[73.77355, 18.526641], [73.773258, 18.521356], [73.77355, 18.516071], [73.774423, 18.510843], [73.775868, 18.50573],
[73.777868, 18.500789], [73.780402, 18.496072], [73.783442, 18.491633], [73.786955, 18.487519], [73.790902, 18.483776],
[73.79524, 18.480444], [73.799921, 18.477561], [73.804894, 18.475158], [73.810105, 18.47326], [73.815497, 18.47189],
[73.82101, 18.471062], [73.826584, 18.470785], [73.832158, 18.471062], [73.837671, 18.47189], [73.843062, 18.47326],
[73.848273, 18.475158], [73.853246, 18.477561], [73.857928, 18.480444], [73.862265, 18.483776], [73.866212, 18.487519],
[73.869725, 18.491633], [73.872765, 18.496072], [73.875299, 18.500789], [73.877299, 18.50573], [73.878744, 18.510843],
[73.879617, 18.516071], [73.879909, 18.521356]]],
"type": "Polygon",
"bbox": [73.773258, 18.470785, 73.879909, 18.571912]
},
"properties": {
"status": "add",
"editable": "true",
"color": {
"r": 0,
"g": 255,
"b": 255,
"a": 0.45
},
"border": {
"b": 255,
"g": 255,
"r": 0,
"a": 1
},
"OBJECTID": "4"
},
"type": "Feature",
"bbox": [73.773258, 18.470785, 73.879909, 18.571912]
}
],
"type": "FeatureCollection"
}

Defining the "reverse" coordinate transformation helped me:
var ctf = new ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory();
layer.CoordinateTransformation = ctf.CreateFromCoordinateSystems(
ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84,
ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WebMercator);
layer.ReverseCoordinateTransformation = ctf.CreateFromCoordinateSystems(
ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WebMercator,
ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84);

Related

Highcharts sankey node without links

I have a highcharts sankey diagram with two sides:
There are situations where some of my nodes have empty links (=with 0 weight). I would like the node to being displayed despite having no link from or to it.
Any chance I can achieve this?
I read on this thread that I have to fake it with weight=1 connexions, I could make the link transparent, and twitch the tooltip to hide those, but that's very painful for something that feels pretty basic.
Maybe a custom call of the generateNode call or something?
Thanks for the help
You can use the following wrap to show a node when the weight is 0.
const isObject = Highcharts.isObject,
merge = Highcharts.merge
function getDLOptions(
params
) {
const optionsPoint = (
isObject(params.optionsPoint) ?
params.optionsPoint.dataLabels : {}
),
optionsLevel = (
isObject(params.level) ?
params.level.dataLabels : {}
),
options = merge({
style: {}
}, optionsLevel, optionsPoint);
return options;
}
Highcharts.wrap(
Highcharts.seriesTypes.sankey.prototype,
'translateNode',
function(proceed, node, column) {
var translationFactor = this.translationFactor,
series = this,
chart = this.chart,
options = this.options,
sum = node.getSum(),
nodeHeight = Math.max(Math.round(sum * translationFactor),
this.options.minLinkWidth),
nodeWidth = Math.round(this.nodeWidth),
crisp = Math.round(options.borderWidth) % 2 / 2,
nodeOffset = column.sankeyColumn.offset(node,
translationFactor),
fromNodeTop = Math.floor(Highcharts.pick(nodeOffset.absoluteTop, (column.sankeyColumn.top(translationFactor) +
nodeOffset.relativeTop))) + crisp,
left = Math.floor(this.colDistance * node.column +
options.borderWidth / 2) + Highcharts.relativeLength(node.options.offsetHorizontal || 0,
nodeWidth) +
crisp,
nodeLeft = chart.inverted ?
chart.plotSizeX - left :
left;
node.sum = sum;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (1) {
// Draw the node
node.shapeType = 'rect';
node.nodeX = nodeLeft;
node.nodeY = fromNodeTop;
let x = nodeLeft,
y = fromNodeTop,
width = node.options.width || options.width || nodeWidth,
height = node.options.height || options.height || nodeHeight;
if (chart.inverted) {
x = nodeLeft - nodeWidth;
y = chart.plotSizeY - fromNodeTop - nodeHeight;
width = node.options.height || options.height || nodeWidth;
height = node.options.width || options.width || nodeHeight;
}
// Calculate data label options for the point
node.dlOptions = getDLOptions({
level: (this.mapOptionsToLevel)[node.level],
optionsPoint: node.options
});
// Pass test in drawPoints
node.plotX = 1;
node.plotY = 1;
// Set the anchor position for tooltips
node.tooltipPos = chart.inverted ? [
(chart.plotSizeY) - y - height / 2,
(chart.plotSizeX) - x - width / 2
] : [
x + width / 2,
y + height / 2
];
node.shapeArgs = {
x,
y,
width,
height,
display: node.hasShape() ? '' : 'none'
};
} else {
node.dlOptions = {
enabled: false
};
}
}
);
Demo:
http://jsfiddle.net/BlackLabel/uh6fp89j/
In the above solution, another node arrangement would be difficult to achieve and may require a lot of modifications beyond our scope of support.
You can consider using mentioned "tricky solution", since might return a better positioning result. This solution is based on changing 0 weight nodes on the chart.load() event and converting the tooltip as well, so it may require adjustment to your project.
chart: {
events: {
load() {
this.series[0].points.forEach(point => {
if (point.weight === 0) {
point.update({
weight: 0.1,
color: 'transparent'
})
}
})
}
}
},
tooltip: {
nodeFormatter: function() {
return `${this.name}: <b>${Math.floor(this.sum)}</b><br/>`
},
pointFormatter: function() {
return `${this.fromNode.name} → ${this.toNode.name}: <b>${Math.floor(this.weight)}</b><br/>`
}
},
Demo:
http://jsfiddle.net/BlackLabel/0dqpabku/

Folium-ChoropletMap issues with key_on: does not overlay the choroplet map

I have a strange issue with the following piece of code:
m10=folium.Map(location=[41.9027835,12.4963655],tiles='openstreetmap',zoom_start=5)
df.reset_index(inplace = True)
folium.Choropleth(
geo_data = df.to_json(),
data = df,
columns=['TERRITORIO', var],
key_on='feature.properties.TERRITORIO',
fill_color='YlGnBu',
fill_opacity=0.6,
line_opacity=1,
nan_fill_color='black',
legend_name= get_title_(file_name),
smooth_factor=0).add_to(m10)
folium.features.GeoJson(df,
name='Labels',
style_function=lambda x: {'color':'transparent','fillColor':'transparent','weight':0},
tooltip=folium.features.GeoJsonTooltip(fields=[var],
aliases = [indicator],
labels=True,
sticky=False
)
).add_to(m10)
I use the same piece of code on two different geodataframes. With the first (smaller) dataframe I have no issues.
However, when I try to do the same with the other one I do not see the choroplet map layer.
This is the first dataset (after the reset of the index):
TERRITORIO ... geometry
0 Nord ... MULTIPOLYGON (((9.85086 44.02340, 9.85063 44.0...
1 Centro ... MULTIPOLYGON (((10.31417 42.35043, 10.31424 42...
2 Mezzogiorno ... MULTIPOLYGON (((8.41112 38.86296, 8.41127 38.8...
This is the second dataset (after the reset of the index)
TERRITORIO ... geometry
0 Abruzzo ... MULTIPOLYGON (((930273.425 4714737.743, 930147...
1 Basilicata ... MULTIPOLYGON (((1073851.435 4445828.604, 10738...
2 Calabria ... MULTIPOLYGON (((1083350.847 4416684.239, 10833...
3 Campania ... MULTIPOLYGON (((1037266.901 4449456.848, 10372...
4 Emilia-Romagna ... MULTIPOLYGON (((618335.211 4893983.160, 618329...
These are insted the json files:
first:
{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"INDICATORE": "Densit\\u00e0 di verde storico", "NOTE": null, "Shape_Area": 57926800546.7, "Shape_Leng": 2670893.51269, "TERRITORIO": "Nord", "UNITA_MISURA": "per 100 m2", "V_2004": null, "V_2005": null, "V_2006": null, "V_2007": null, "V_2008": null, "V_2009": null, "V_2010": null, "V_2011": 2.4, "V_2012": 2.4, "V_2013": 2.4, "V_2014": 2.4, "V_2015": 2.4, "V_2016": 2.4, "V_2017": 2.4, "V_2018": 2.4, "V_2019": null, "index": 0}, "geometry": {"type": "MultiPolygon", "coordinates": ...
second:
{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"INDICATORE": "Densit\\u00e0 e rilevanza del patrimonio museale", "NOTE": null, "Shape_Area": 10831496151.0, "Shape_Leng": 664538.009079, "TERRITORIO": "Abruzzo", "UNITA_MISURA": "per 100 km2", "V_2004": null, "V_2005": null, "V_2006": null, "V_2007": null, "V_2008": null, "V_2009": null, "V_2010": null, "V_2011": null, "V_2012": null, "V_2013": null, "V_2014": null, "V_2015": 0.22, "V_2016": null, "V_2017": 0.13, "V_2018": 0.11, "V_2019": null}, "geometry": {"type": "MultiPolygon", "coordinates":...
I really do not understand why one works and the other does not.
Do you have any suggestion?
Thank you in advance for your time!
I can't find any geometry for Nord, Centro, Mezzogiorno Italy, so have sythesized by dissolving regions geometry
have setup functions and variables used by your code to make this a MWE
can switch between geometries by # regions==False, north/central/south==True if True: both generate appropriate folium maps
it's clear in your question that your two sets of geometry are using different CRS. First data set looks like EPSG:4326 (hence works). Second looks like a UTM CRS (points in meters not degrees) that would need to be projected to EPSG:4326
import folium
import geopandas as gpd
import numpy as np
# make SO code runnable, get some geometry and set columns / variables used by code
df = gpd.read_file("https://github.com/openpolis/geojson-italy/raw/master/geojson/limits_IT_regions.geojson").sort_values("reg_name")
df["var"] = np.random.randint(1,10,len(df))
df["TERRITORIO"] = df["reg_istat_code_num"]
df["NCM"] = np.where(df["reg_istat_code_num"]<9,"Nord", np.where(df["reg_istat_code_num"]<15,"Centro", "Mezzogiorno"))
var = "var"
file_name="regions"
indicator = "some number"
def get_title_(file_name):
return file_name
# regions==False, north/central/south==True
if True:
df = df.dissolve("NCM")
file_name = "ncm"
# unchanged code
m10 = folium.Map(location=[41.9027835, 12.4963655], tiles="openstreetmap", zoom_start=5)
df.reset_index(inplace=True)
folium.Choropleth(
geo_data=df.to_json(),
data=df,
columns=["TERRITORIO", var],
key_on="feature.properties.TERRITORIO",
fill_color="YlGnBu",
fill_opacity=0.6,
line_opacity=1,
nan_fill_color="black",
legend_name=get_title_(file_name),
smooth_factor=0,
).add_to(m10)
folium.features.GeoJson(
df,
name="Labels",
style_function=lambda x: {
"color": "transparent",
"fillColor": "transparent",
"weight": 0,
},
tooltip=folium.features.GeoJsonTooltip(
fields=[var], aliases=[indicator], labels=True, sticky=False
),
).add_to(m10)
m10
Modifying the crs I was able to overcome the above issiue.
# Create the folium map
m10=folium.Map(location=[41.9027835,12.4963655],tiles='openstreetmap',zoom_start=5)
# Data
df.to_crs(crs = 4326, inplace = True)
df.reset_index(inplace = True)
folium.Choropleth(
geo_data = df.to_json(),
data = df,
columns=['TERRITORIO', var],
key_on='feature.properties.TERRITORIO',
fill_color='YlGnBu',
fill_opacity=0.6,
line_opacity=1,
nan_fill_color='black',
legend_name= get_title_(file_name),
smooth_factor=0).add_to(m10)

Dynamic cell color in jspdf autoTable?

Is it possible to define the cell color with a nested property of the object mapped to the table ?
The JSON structure of the objects is :
objects: [
{
"agent": "agent_1",
"days": {
day_0: {
"code": "ABC",
"color": "#0062cc"
},
day_1: {
"code": "DEF",
"color": "#a09494b2"
}
},
{
[...]
}
]
I have a table defined like this :
let columns = [
{title: "Agent", dataKey: "agent"},
{title: "january 1st", dataKey: "day_0"},
{title: "january 2nd", dataKey: "day_1"}]
let rows = [
{agent: "agent_1", day_0: "ABC", day_1: "DEF"},
[...]
]
All that works fine. But I'd like to set the color of each day cell dynamically, set with the color code of the corresponding object. Something like :
createdCell: function(cell, data) {
{
cell.styles.fillColor = "day_0.color";
}
}
But I can't figure how to pass the data to the table. Is it possible ? Can displayProperty help in any way ?
EDIT: In this case it was that v2.3.4 of jspdf-autotable was needed
Based on our comments discussion I think I understood your problem. You can try something like this (with the hexToRgb function from here)
let columns = [{
title: "Agent",
dataKey: "agent"
},
{
title: "january 1st",
dataKey: "day_0"
},
{
title: "january 2nd",
dataKey: "day_1"
}
]
let objects = [{
agent: "agent_1",
day_0: {
"code": "ABC",
"color": "#00ff00"
},
day_1: {
"code": "DEF",
"color": "#ff0000"
}
// etc
}];
let doc = jsPDF()
doc.autoTable(columns, objects, {
createdCell: function(cell, data) {
let hex = cell.raw.color
if (hex) {
let rgb = hexToRgb(hex)
cell.styles.fillColor = rgb;
cell.text = cell.raw.code
}
}
});
doc.save('jhg.pdf')
function hexToRgb(hex) {
var bigint = parseInt(hex.replace('#', ''), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
return [r, g, b];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.4.1/jspdf.debug.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/2.3.4/jspdf.plugin.autotable.js"></script>
I just leave the answer here just in case anyone needs it.
We can use the didParseCell hook.
doc.autoTable({
head: [[..., color]],
body: [[..., #ffffff], [..., #ff0000]], // pass hexa value to the cell
didParseCell: function (HookData) {
if (HookData.cell == undefined)
return;
// find cell taht contains the hexa value
// the change the fillColor property
// and set the cell value to empty
var color = HookData.cell.text[0];
if (color.match(/^#[a-fA-F0-9]{3}([a-fA-F0-9]{3})/g) != null) {
HookData.cell.styles.fillColor = hexToRgb(color);
HookData.cell.text = [];
}
}
});
Code to convert hexa to RGB:
hexToRgb(hex) {
var bigint = parseInt(hex.replace('#', ''), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
return [r, g, b];
}
Package version:
jspdf : 2.5.1
jspdf-autotable :3.5.25

Highcharts Sunburst levels radius

I'm making a sunburst with Highcharts .NET,
This is how i setup the chart:
Highcharts higcharts = new Highcharts
{
Chart = new Chart
{
Type = ChartType.Sunburst,
Width = 700,
Height = 700
},
Title = new Title
{
Text = "Monthly Average Temperature",
X = -20
},
Subtitle = new Subtitle
{
Text = "Source: WorldClimate.com",
X = -20
},
Legend = new Legend
{
Layout = LegendLayout.Vertical,
Align = LegendAlign.Right,
VerticalAlign = LegendVerticalAlign.Middle,
BorderWidth = 0
},
Series = new List<Series>
{
new SunburstSeries
{
Name ="Test",
Data = data,
//LevelSize = new SunburstSeriesLevelSize
//{
// Unit = SunburstSeriesLevelSizeUnit.Percentage,
// Value = 100
//},
Levels = new List<SunburstSeriesLevels>
{
new SunburstSeriesLevels
{
LevelSize = new SunburstSeriesLevelsLevelSize{
Unit = SunburstSeriesLevelsLevelSizeUnit.Percentage,
Value = 90
}
},
new SunburstSeriesLevels
{
LevelSize = new SunburstSeriesLevelsLevelSize{
Unit = SunburstSeriesLevelsLevelSizeUnit.Percentage,
Value = 10
}
}
}
}
}
};
I tried many ways but the levels radius never change, did i miss something?
The only one working is the levelsize of the entire serie but i need to set the size for a specific level.
I tried to search but it looks like nobody already encountered any problem.
Level's object levelSize is able do control the size of individual level. It has two properties: unit (pixels / percentage / weight) and value (determined by the unit):
levels: [{
level: 1,
levelIsConstant: false,
levelSize: {
unit: 'pixels',
value: 30
}
}, {
level: 2,
colorByPoint: true,
dataLabels: {
rotationMode: 'parallel'
}
}, {
level: 3,
levelIsConstant: true,
levelSize: {
unit: 'weight',
value: 2
}
}, {
level: 4,
levelIsConstant: true,
levelSize: {
unit: 'percentage',
value: 30
}
}]
Live demo: http://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/sunburst-levelsize/
API reference: https://api.highcharts.com/highcharts/series.sunburst.levelSize

How to transform a point with latitude and longitude value to a coordinates in highcharts map?

Highmap metadata has an option 'hc-transform',can someone explain how to use it?
I used the default China map geodata in highmaps,but now I want to add a new point,for example [lat,lon] [121,23],I want to know how can I transform it to a coordinate.
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:EPSG:3415"
}
},
"hc-transform": {
"cn-all": {
"xpan": 350,
"ypan": 25,
"hitZone": {
"type": "Polygon",
"coordinates": [
[
[7902,3046],
[7929,3041],
[7947,3014],
[7915,2972]
]
]
},
"crs": "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +toWGS84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +vunits=m +no_defs",
"scale": 0.000129831107685,
"jsonres": 15.5,
"jsonmarginX": -999,
"jsonmarginY": 9851.0,
"xoffset": -3139937.49309,
"yoffset": 4358972.7486
}
}
Here is what I do.
var transform = Highcharts.maps['countries/cn/cn-all']['hc-transform']['default'];
var position = $(".chart").highcharts().transformFromLatLon({ lat: 121, lon: 23 }, transform);
console.log(position); // {x:NaN ,y:NaN}
Am I wrong in 'hc-transform' setting? How should I make the case.
The 'hc-transform' setting is right.
please try switch lat and lon, like this:
$(".chart").highcharts().transformFromLatLon({ lat: 121, lon: 23 }, transform);
then x and y come out.

Resources