Maintaining the topology on a MultiLineString - topojson

I'm trying to get topological movement of lines with Leaflet-Geoman plugin using TopoJSON. There is a method called topojson.mesh , which
Returns the GeoJSON MultiLineString geometry object representing the mesh for the specified object in the given topology. This is useful for rendering strokes in complicated objects efficiently, as edges that are shared by multiple features are only stroked once. If object is not specified, a mesh of the entire topology is returned.
Thanks to answer in this post, I've been able to return the MultiLineString using topojson.mesh. Since Leaflet-Geoman supports MultiLineString I came across with idea that may be the returned mesh can be edited with Leaflet-Geoman while maintaining the topological properties.
But when I try to accomplish it, the returned MultiLineString get separated in to two parts when I try to edit it using geoman plugin. My question is if that it is really a mesh that returned from topojson.mesh why the lines get seperated? Does that cause by geoman plugin? If that so, how can I get it done? Is there any way I can change the position of a node by dragging it while maintaining the topology?
I'll attach the code below
<!DOCTYPE html>
<html>
<head>
<title>Topology Test</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="src/css/leaflet.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<link rel="stylesheet" href="https://unpkg.com/#geoman-io/leaflet-geoman-free#latest/dist/leaflet-geoman.css" />
<style>
#mapdiv {
height: 899px;
background-color: #acd6e2;
}
</style>
</head>
<body>
<div id="mapdiv"></div>
<script src="https://unpkg.com/topojson#3"></script>
<script src="src/js/leaflet-src.js"></script>
<script src="https://unpkg.com/#geoman-io/leaflet-geoman-free#latest/dist/leaflet-geoman.min.js"></script>
<script>
var mymap = L.map('mapdiv', {
layers: [
new L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
'attribution': 'Map data © OpenStreetMap contributors'
})
],
});
mymap.pm.addControls({
position: 'topleft',
drawCircle: false,
});
fetch("data/data.geojson")
.then(res => res.json())
.then(json => {
//var layer = L.geoJSON(json).addTo(map);
var topo = topojson.topology([json]);
console.log(json, topo, topojson.mesh(topo));
var layerLines = L.geoJson(topojson.mesh(topo), {
fill: false,
}).addTo(mymap);
mymap.fitBounds(layerLines.getBounds());
});
</script>
</body>
</html>
data.geojson
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-113,
37
],
[
-113,
40
],
[
-109,
40
],
[
-109,
37
],
[
-113,
37
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-109,
37
],
[
-109,
39
],
[
-104,
39
],
[
-104,
37
],
[
-109,
37
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-109,
34
],
[
-109,
37
],
[
-102,
37
],
[
-102,
34
],
[
-109,
34
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-104,
37
],
[
-104,
40
],
[
-100,
40
],
[
-100,
37
],
[
-104,
37
]
]
]
}
}
]
}

For anyone who is looking for an answer to this type of question, I found a method using OpenLayers v6.5.0. Their is an example for Draw and Modify Features, which can maintain the topology of lines and polygons.
Hope this helps some one :)

Related

Elasticsearch ingest circle processor problem

I'm trying to insert a location that is using a circle processor to generate the circle points. Location [30, 10] in the document is generated with the circle points properly.
docs: Circle processor | Elasticsearch Guide [8.5] | Elastic
PUT _ingest/pipeline/polygonize_circles
{
"description": "translate circle to polygon",
"processors": [
{
"circle": {
"field": "circle",
"error_distance": 1,
"shape_type": "geo_shape"
}
}
]
}
PUT circles
{
"mappings": {
"properties": {
"circle": {
"type": "geo_shape"
}
}
}
}
PUT circles/_doc/2?pipeline=polygonize_circles
{
"circle": {
"type": "circle",
"radius": "40m",
"coordinates": [35.539917, -78.472000]
}
}
GET circles/_doc/2
But if I use another location. The generated coordinate looks like an oval with the wrong radius.
my location [35.54171753710938, -78.472]
created coordinates:
"circle": {
"coordinates": [
[
[
35.54171753710938,
-78.472
],
[
35.54112406581135,
-78.47173430472324
],
[
35.540630197847186,
-78.47167003953963
],
[
35.540375564960186,
-78.47165140797998
],
[
35.54021828908823,
-78.47164529506406
],
[
35.54010640465818,
-78.47164274491611
],
[
35.54001650261309,
-78.47164162397662
],
[
35.53993651647515,
-78.47164003979641
],
[
35.539858062238046,
-78.47164049555624
],
[
35.53977439153409,
-78.47164207973975
],
[
35.5396750942597,
-78.47164321572573
],
[
35.5395458932956,
-78.47164846905713
],
[
35.539348254773515,
-78.47165779735127
],
[
35.53899878746994,
-78.47169061817682
],
[
35.53833938849573,
-78.47182842440924
],
[
35.53833938849573,
-78.47217157559075
],
[
35.53899878746994,
-78.47230938182317
],
[
35.539348254773515,
-78.47234220264872
],
[
35.5395458932956,
-78.47235153094286
],
[
35.5396750942597,
-78.47235678427425
],
[
35.53977439153409,
-78.47235792026024
],
[
35.539858062238046,
-78.47235950444374
],
[
35.53993651647515,
-78.47235996020358
],
[
35.54001650261309,
-78.47235837602337
],
[
35.54010640465818,
-78.47235725508388
],
[
35.54021828908823,
-78.47235470493592
],
[
35.540375564960186,
-78.47234859202001
],
[
35.540630197847186,
-78.47232996046036
],
[
35.54112406581135,
-78.47226569527675
],
[
35.54171753710938,
-78.472
]
]
],
"type": "Polygon"
}
coordinates mapping on google maps
Is it an issue or It's working as expected? Because the coordinates are not a circle so it's impacting the search result.
You need to specify your coordinate array using longitude first and then latitude, I think you did the opposite and your circle is in the middle of Antartica.
If you do it like this:
PUT circles/_doc/2?pipeline=polygonize_circles
{
"circle": {
"type": "circle",
"radius": "40m",
"coordinates": [-78.472000, 35.539917]
}
}
Then your circle doesn't look oval anymore:
From the official doc:
In GeoJSON and WKT, and therefore Elasticsearch, the correct coordinate order is longitude, latitude (X, Y) within coordinate arrays. This differs from many Geospatial APIs (e.g., Google Maps) that generally use the colloquial latitude, longitude (Y, X).

Elasticsearch geospatial map, not able to render Linestring

the elasticsearch index contains json as below, only relevant element is show
"geoLocation": {
"coordinates": [ [ -90.66487121582031, 42.49201965332031 ], [ -90.66487884521484, 42.49202346801758 ], [ -90.6648941040039, 42.492034912109375 ], [ -90.66490936279297, 42.49203872680664 ], [ -90.66492462158203, 42.492042541503906 ], [ -90.6649398803711, 42.49204635620117 ], [ -90.66495513916016, 42.49205017089844 ], [ -90.66497039794922, 42.4920539855957 ], [ -90.66498565673828, 42.492061614990234 ], [ -90.66500854492188, 42.492061614990234 ], [ -90.66502380371094, 42.49207305908203 ], [ -90.6650390625, 42.4920654296875 ] ],
"type": "linestring"
},
The template for generating the mapping is as below
PUT _template/template_1?include_type_name=true
{
"index_patterns": ["metromind-its-alerts-day2-*"],
"settings": {
"number_of_shards": 2
},
"mappings": {
"logs": {
"properties": {
"geoLocation": {
"type": "geo_shape"
}
}
}
}
}
the mapping generated is shown below
mapping showing the geoLocation type
When Kibana Maps are used it detects the geo_shape
Kibana Map to render Linestring
Note However no Linestring is rendered, please suggest the resolution
The line string is there, in Dubuque IL, it's just that it's extra small at the scale of the earth.
Just click on the following icon and Elastic Map will focus on it and you'll see it:

Parquet File upload in Druid

I am new to the Druid.
I have done a local setup of druid in local and I am able to load files of json data in druid.
However when I am trying to upload a parquet file, it is giving Unexpected character exception.
I've installed Parquet and Avro extensions and I get following error in each case
$ >curl -X 'POST' -H 'Content-Type:application/json' -d #examples/wikipedia_hadoop_parquet_job.json http://localhost:8090/druid/indexer/v1/task
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 500 </title>
</head>enter code here
<body>
<h2>HTTP ERROR: 500</h2>
<p>Problem accessing /druid/indexer/v1/task. Reason:
<pre> javax.servlet.ServletException: com.fasterxml.jackson.core.JsonParseException: Unexpected character (&apos;}&apos; (code 125)): was expecting double-quote to start field name
at [Source: HttpInputOverHTTP#57df7615[c=2032,q=1,[0]=EOF,s=STREAM]; line: 1, column: 493]</pre></p>
<hr />Powered by Jetty:// 9.3.19.v20170502<hr/>
</body>
</html>
Below is the JSON config file
{
"type": "index_hadoop",
"spec": {
"ioConfig": {
"type": "hadoop",
"inputSpec": {
"type": "static",
"inputFormat": "org.apache.druid.data.input.parquet.DruidParquetInputFormat",
"paths": "example/wikipedia_list.parquet"
},
"metadataUpdateSpec": {
"type": "postgresql",
"connectURI": "jdbc:postgresql://localhost/druid",
"user" : "druid",
"password" : "asdf",
"segmentTable": "druid_segments"
},
},
"dataSchema": {
"dataSource": "wikipedia",
"parser": {
"type": "parquet",
"parseSpec": {
"format": "timeAndDims",
"timestampSpec": {
"column": "timestamp",
"format": "auto"
},
"dimensionsSpec": {
"dimensions": [
"page",
"language",
"user",
"unpatrolled"
],
"dimensionExclusions": [],
"spatialDimensions": []
}
}
},
"metricsSpec": [{
"type": "count",
"name": "count"
}, {
"type": "doubleSum",
"name": "deleted",
"fieldName": "deleted"
}, {
"type": "doubleSum",
"name": "delta",
"fieldName": "delta"
}],
"granularitySpec": {
"type": "uniform",
"segmentGranularity": "DAY",
"queryGranularity": "NONE",
"intervals": ["2013-08-30/2013-09-02"]
}
},
"tuningConfig": {
"type": "hadoop",
"workingPath": "tmp/working_path",
"partitionsSpec": {
"targetPartitionSize": 5000000
},
"leaveIntermediate": true
}
}
}
I am not able to figure out the issue. Let me know, if I am missing something.

D3.js nesting and rollup at the same time in v4

I have a very similar task as D3.js nesting and rollup at same time and solution provided by #altocumulus for d3.js v3 works perfectly fine for me (tested). However, I am using d3.js v4 across my website and I am having difficulties replicating the same approach with v4 - I am not getting the same results. Perhaps, because I don't understand the sumChildren function. Please feel free to provide a better or different approach on how to restructure the loaded csv file as json with subtotals at every node level using d3.js v4. In my case, I need to have Population at the Country, State and the City levels.
Disclaimer: I have been using SO for many years and in most of the cases I got my answers from questions posted by other people, but this is my first question. In addition to this I am noob in d3.js
population.csv:
Country,State,City,Population
"USA","California","Los Angeles",18500000
"USA","California","San Diego",1356000
"USA","California","San Francisco",837442
"USA","Texas","Austin",885400
"USA","Texas","Dallas",1258000
"USA","Texas","Houston",2196000
index.html:
<!DOCTYPE html>
<html>
<head>
<title> Test D3.js</title>
</head>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.4.0/d3.min.js"></script>
<body>
<script>
d3.csv("population.csv", function(error, data) {
if (error) throw error;
var nested = d3.nest()
.key(function(d) { return d.Country; })
.key(function(d) { return d.State; })
.rollup(function(cities) {
return cities.map(function(c) {
return {"City": c.City, "Population": +c.Population };
});
})
.entries(data);
// Recursively sum up children's values
function sumChildren(node) {
node.Population = node.values.reduce(function(r, v) {
return r + (v.values ? sumChildren(v) : v.Population);
},0);
return node.Population;
}
// Loop through all top level nodes in nested data,
// i.e. for all countries.
nested.forEach(function(node) {
sumChildren(node);
});
// Output. Nothing of interest below this line.
d3.select("body").append("div")
.style("font-family", "monospace")
.style("white-space", "pre")
.text(JSON.stringify(nested,null,2));
});
</script>
</body>
</html>
Results:
[
{
"key": "USA",
"values": [
{
"key": "California",
"value": [
{
"City": "Los Angeles",
"Population": 18500000
},
{
"City": "San Diego",
"Population": 1356000
},
{
"City": "San Francisco",
"Population": 837442
}
]
},
{
"key": "Texas",
"value": [
{
"City": "Austin",
"Population": 885400
},
{
"City": "Dallas",
"Population": 1258000
},
{
"City": "Houston",
"Population": 2196000
}
]
}
],
"Population": null
}
]
Desired Results:
[
{
"key": "USA",
"values": [
{
"key": "California",
"values": [
{
"City": "Los Angeles",
"Population": 18500000
},
{
"City": "San Diego",
"Population": 1356000
},
{
"City": "San Francisco",
"Population": 837442
}
],
"Population": 20693442
},
{
"key": "Texas",
"values": [
{
"City": "Austin",
"Population": 885400
},
{
"City": "Dallas",
"Population": 1258000
},
{
"City": "Houston",
"Population": 2196000
}
],
"Population": 4339400
}
],
"Population": 25032842
}
]
The v4 changelog tells us that
When used in conjunction with nest.rollup, nest.entries now returns {key, value} objects for the leaf entries, instead of {key, values}.
It is this little renaming from values to value within the leaf nodes which eventually breaks the code. Changing the helper function accordingly should get you back on track:
// Recursively sum up children's values
function sumChildren(node) {
if (node.value) {
node.values = node.value; // Ensure, leaf nodes will also have a values array
delete node.value; // ...instead of a single value
}
node.Population = node.values.reduce(function(r, v) {
return r + (v.value? sumChildren(v) : v.Population);
},0);
return node.Population;
}

Using d3.nest() with geojson files

How is d3.nest() used with geojson files?
My geojson data is formatted as follows:
"features": [
{ "type": "Feature", "properties": { "neighborhood": "Allerton", "boroughCode": "2", "borough": "Bronx", "#id": "http:\/\/nyc.pediacities.com\/Resource\/Neighborhood\/Allerton" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -73.848597000000183, 40.871670000000115 ], [ -73.845822536836778, 40.870239076236174 ], [ -73.854559184633743, 40.859953835764252 ], [ -73.854665433068263, 40.859585694988056 ], [ -73.856388703358959, 40.857593635304482 ], [ -73.868881809153407, 40.857223150158326 ], [ -73.868317552728243, 40.857862062258313 ], [ -73.869553714672321, 40.857784095600181 ], [ -73.871024857620654, 40.857309948816905 ], [ -73.870480549987164, 40.865413584098484 ], [ -73.87055489856489, 40.869702798589863 ], [ -73.86721594442561, 40.869689663636713 ], [ -73.85745, 40.869533000000182 ], [ -73.855550000000108, 40.871813000000145 ], [ -73.853597967576576, 40.873288368674203 ], [ -73.848597000000183, 40.871670000000115 ] ] ] } }
But my nest command:
var nested_data = d3.nest()
.key(function(d, i) { console.log(d); return d.features.properties.neighborhood; })
.entries(map);
returns an empty array.
I want to nest my data to more easily filter it. Is this advised?
Assuming your geojson looks like the below
var map = {
type: "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"neighborhood": "Allerton",
"boroughCode": "2",
"borough": "Bronx",
"#id": "http:\/\/nyc.pediacities.com\/Resource\/Neighborhood\/Allerton"
},
"geometry": { /* various coordinates, etc */ }
]
}
So, what you want to do is:
d3.nest()
.key(function(d, i) {
return d.properties.neighborhood;
})
.entries(map.features);
You want to pass map.features since that's your array.

Resources