Excluding last element of array with slice - javascript
In an application I am working with arrays of coordinates - when I log the points of feature to the console, I am returned an array of 5 coordinate pairs:
var poly = data.features[0].geometry.coordinates
console.log(poly)
However, when I use slice() to exclude the last element of the array - which should return 4 instead of 5 - I am returned a blank array
var poly = data.features[0].geometry.coordinates.slice(0, -1);
console.log(poly)
Why does this happen and why doesn't the slice() method work in this case?
Poly is an array with a single element, what you really want is
var poly = data.features[0].geometry.coordinates[0].slice(0, -1);
It looks like you are using GeoJSON data and you want to strip off the duplicate coordinate at the end of a ring. This being the case, you should be aware that your coordinates array (the one with a single element that has the array of points inside it) may actually contain more than one element. In GeoJSON, a polygon is not the same concept as in geometry. A geographic "polygon" consists of an outer "ring" and zero or more inner rings. A "ring" is like a polygon in geometry.
The first ring in a polygon - the single ring that your example has - is the "outer ring", that is the outer boundary of the area you're mapping. Any additional rings are "inner rings" which represent holes that are excluded from the area. For example, a state or county with two lakes in it might be represent as a polygon with three rings, one for the outer boundary and the other two for each of the lakes.
Also, are you checking geometry.type before assuming that geometry.coordinates is a Polygon at all? You may unexpectedly receive a MultiPolygon, which is an array of polygons, so now you have one more level of nested arrays. GeoJSON data sources will often use either a Polygon or MultiPolygon as needed.
Consider the state of Utah. It has a single outer boundary, and of course has the Great Salt Lake inside it (along with some smaller lakes). So Utah can be represented by a Polygon with outer ring for the state border and inner rings for the Great Salt Lake and other lakes.
But what about Hawaii? It consists of several islands. There is no single outer boundary for the state. Each island requires its own outer boundary. So Hawaii has to be represented by a MultiPolygon, i.e. an array of Polygon arrays. Each island gets its own Polygon.
Whatever code you're writing, it should take all this into account. You should distinguish between a Polygon and a MultiPolygon and for the latter, process each Polygon that makes up the MultiPolygon. And if you're stripping off the last element from each polygon's outer ring, you probably want to do that for the inner rings as well. For more details, see the GeoJSON RFC.
I would provide some sample code to do all this, but I'm not clear enough on what you're working toward. But this should at least give you some direction.
As you can see on your screenshot,
var poly = data.features[0].geometry.coordinates
returns [Array(5)] which is essentially a two dimensional array. To exclude the last element in the nested array, you'll need to do
// coordinates[0] returns the first item in the array which is Array(5)
var poly = data.features[0].geometry.coordinates[0].slice(0, -1);
To keep a two dimensional array minus the last coordinate pair instead, you need to recursively clone each element.
var poly = data.features[0].geometry.coordinates.map(function(val) {
return val.slice(0, -1);
});
Related
Each LinearRing of a Polygon must have 4 or more Positions with turf.mask() for JavaScript
I am trying to use turf.mask() on these two polygons: state and hexagon. state = {"type":"Feature","properties":{},"geometry":{"type":"MultiPolygon","coordinates":[[[[-122.5399362859679,37.830838250358305],[-122.5352992,37.8292543],[-122.5360944,37.8239044],[-122.5279709,37.8153353],[-122.5292904,37.8198549],[-122.5229995,37.8251486],[-122.5099818,37.8245382],[-122.4995901,37.8199115],[-122.4907812,37.8266974],[-122.478306,37.8254268],[-122.47823831099781,37.83244271995018],[-122.5399362859679,37.83244271995018],[-122.5399362859679,37.830838250358305]]],[[[-122.5209211,37.5943953],[-122.5172512,37.5892786],[-122.5205505,37.5739919],[-122.5144188,37.5657519],[-122.5135889,37.5559887],[-122.5197357,37.5371873],[-122.5179299,37.5238199],[-122.5118361,37.5131343],[-122.4987459,37.5009924],[-122.4993886,37.4956524],[-122.4934399,37.4929939],[-122.4845619,37.493956],[-122.4933403,37.4931535],[-122.4963325,37.4978499],[-122.4870092,37.5037473],[-122.4837393,37.5005349],[-122.485041,37.5028707],[-122.4795964,37.5033699],[-122.4805619,37.5005397],[-122.4770952,37.5024601],[-122.4745322,37.5006196],[-122.4858172,37.49523],[-122.4706747,37.5007852],[-122.4584325,37.4908973],[-122.4508972389485,37.47800498084561],[-122.2721445405403,37.47800498084561],[-122.2721445405403,37.57062405456298],[-122.2734391,37.5701746],[-122.2845216,37.5757537],[-122.2915515,37.5743495],[-122.2933973,37.5691119],[-122.2968954,37.5755277],[-122.305437,37.5749218],[-122.316048,37.5840024],[-122.3139154,37.5900472],[-122.3165712,37.5867603],[-122.3173273,37.5903678],[-122.3177269,37.5867609],[-122.318293,37.5911926],[-122.3145503,37.5923252],[-122.3325163,37.5877861],[-122.3344629,37.5920779],[-122.3380163,37.5920507],[-122.3384833,37.5871208],[-122.3579245,37.5888057],[-122.3383946,37.5882378],[-122.3384111,37.5919623],[-122.3459053,37.5898998],[-122.3439243,37.5920571],[-122.360012,37.592015],[-122.3640642,37.598455],[-122.3786677,37.6059181],[-122.3713687,37.6147206],[-122.3582017,37.6089833],[-122.3548517,37.6148603],[-122.3681936,37.6210318],[-122.3653327,37.6281401],[-122.3736554,37.6279567],[-122.3789373,37.6315371],[-122.3861757,37.6288252],[-122.3878968,37.634473],[-122.3796783,37.6351943],[-122.3837736,37.6399334],[-122.3911844,37.6391024],[-122.3909661,37.6444244],[-122.3917268,37.6390208],[-122.3965553,37.6388167],[-122.3934705,37.6438287],[-122.3992832,37.642004],[-122.4028452,37.6426094],[-122.4013169,37.6461099],[-122.3994533,37.6431429],[-122.396963,37.6427665],[-122.3917379,37.6477991],[-122.3803373,37.648125],[-122.3755326,37.6549592],[-122.3799605,37.6607485],[-122.3744577,37.6624522],[-122.3829018,37.6631099],[-122.3811718,37.6681459],[-122.3859696,37.667591],[-122.3869568,37.6645382],[-122.3952727,37.6650535],[-122.3874281,37.6716869],[-122.3810763,37.6718878],[-122.3811199,37.6773246],[-122.3878163,37.6783848],[-122.3941205,37.7073575],[-122.3914199,37.7096391],[-122.3744129,37.7086766],[-122.3800854,37.7106027],[-122.3759862,37.7160655],[-122.3869772,37.7242593],[-122.3808547,37.7217156],[-122.3768873,37.7239002],[-122.3738477,37.7188103],[-122.3654008,37.7160347],[-122.3627284,37.7200714],[-122.3590965,37.7193159],[-122.3652453,37.7256766],[-122.3577914,37.7246098],[-122.3616456,37.7251817],[-122.3569649,37.728662],[-122.3622605,37.7284968],[-122.3588648,37.7297956],[-122.3658749,37.7331392],[-122.3754819,37.7328043],[-122.3764548,37.739765],[-122.3732029,37.7372296],[-122.3675347,37.7382209],[-122.3735546,37.7386854],[-122.3676515,37.740137],[-122.3752341,37.7468542],[-122.3929128,37.7474166],[-122.3757592,37.7487044],[-122.3761037,37.752267],[-122.3841093,37.7546193],[-122.3808144,37.755204],[-122.3838352,37.7620693],[-122.3810399,37.762589],[-122.3870406,37.7637898],[-122.3856126,37.7702142],[-122.3831784,37.7697199],[-122.3867755,37.7732761],[-122.381492,37.7719454],[-122.381849,37.7746281],[-122.3869477,37.7743538],[-122.3850142,37.776339],[-122.3899459,37.7766762],[-122.398671,37.7705013],[-122.3902987,37.7772108],[-122.3874581,37.7783059],[-122.3877716,37.7822999],[-122.3847049,37.7825645],[-122.3876263,37.7854527],[-122.3845243,37.7873797],[-122.3874381,37.7872449],[-122.3852995,37.7898012],[-122.3976619,37.7998545],[-122.3957218,37.8014672],[-122.3983318,37.8006011],[-122.3971208,37.8030526],[-122.3997229,37.8021723],[-122.3985174,37.8045967],[-122.4009866,37.8035636],[-122.4003819,37.8072585],[-122.4028024,37.8060243],[-122.4043847,37.8088919],[-122.4053268,37.8068875],[-122.4104297,37.811425],[-122.4105114,37.8087706],[-122.4203547,37.8115306],[-122.4163293,37.80833],[-122.4228214,37.8102504],[-122.4210749,37.8085664],[-122.424679,37.8066449],[-122.4301758,37.809232],[-122.4328481,37.8057039],[-122.435879,37.8075603],[-122.4473666,37.8055087],[-122.440031,37.808853],[-122.4632009,37.8050656],[-122.4777987,37.8109716],[-122.4869244,37.789907],[-122.5059955,37.7881687],[-122.5147128,37.7811281],[-122.5070212,37.7235595],[-122.4963102,37.6820786],[-122.4941412,37.6435021],[-122.4960688,37.6232689],[-122.4990205,37.6201788],[-122.4969547,37.6120108],[-122.5020589,37.6067327],[-122.5006007,37.6013344],[-122.5060876,37.5963957],[-122.5149418,37.5987038],[-122.5151511,37.5958341],[-122.5209211,37.5943953]]],[[[-122.47454130078302,37.83244271995018],[-122.4727523,37.8320753],[-122.4726672512427,37.83244271995018],[-122.47454130078302,37.83244271995018]]],[[[-122.379047,37.8267146],[-122.3713678,37.8151916],[-122.3727963,37.8108587],[-122.3619928,37.8070656],[-122.3589024,37.8147697],[-122.3672433,37.8122518],[-122.3704001,37.8142932],[-122.3629308,37.8226056],[-122.3686209,37.8310775],[-122.3733669,37.8322631],[-122.379047,37.8267146]]],[[[-122.3426269,37.8057313],[-122.3413237,37.803655],[-122.3249395,37.8067225],[-122.3252406,37.8000102],[-122.330053,37.799951],[-122.3055583,37.7937247],[-122.2805644,37.7957313],[-122.2721445405403,37.79148781862211],[-122.2721445405403,37.83244271995018],[-122.29662795299252,37.83244271995018],[-122.2950082,37.8292417],[-122.3055254,37.826014],[-122.3148745,37.8257845],[-122.3119885,37.82721],[-122.3160071,37.8278395],[-122.3214924,37.8230959],[-122.3300331,37.8216442],[-122.3188547,37.8187413],[-122.3151503,37.8218637],[-122.3083022,37.8210817],[-122.322915,37.8101466],[-122.3330683,37.8110303],[-122.3426269,37.8057313]]],[[[-122.3316381,37.7814097],[-122.3075012,37.7760617],[-122.3082387,37.7803977],[-122.298304,37.7800123],[-122.3011353,37.7742605],[-122.2914341,37.7681259],[-122.2815741,37.7711087],[-122.280682,37.7662999],[-122.276538,37.768255],[-122.2721445405403,37.76325713296079],[-122.2721445405403,37.78664847083194],[-122.2783834,37.7918049],[-122.3005534,37.790309],[-122.3314733,37.7968291],[-122.3316381,37.7814097]]],[[[-122.2887255,37.7656186],[-122.2827173,37.763773],[-122.2871204,37.7658696],[-122.2842014,37.7673989],[-122.2860548,37.7695738],[-122.2887255,37.7656186]]]]}} hex = {"type":"Feature","properties":{},"geometry":{"type":"MultiPolygon","coordinates":[[[[-121.91508032705622,37.271355866731895],[-121.86222328902491,37.353926450852256],[-121.92354999630157,37.42834118609435],[-122.03773496427027,37.42012867767778],[-122.09042892904397,37.33755608435298],[-122.02910130918998,37.26319797461824],[-121.91508032705622,37.271355866731895]]],[[[-122.09042892904397,37.33755608435298],[-122.03773496427027,37.42012867767778],[-122.0991572595036,37.49436145881944],[-122.21327339445754,37.485965058551095],[-122.2658032483195,37.4033911145053],[-122.20438138645652,37.32921488696578],[-122.09042892904397,37.33755608435298]]],[[[-122.2658032483195,37.4033911145053],[-122.21327339445754,37.485965058551095],[-122.27479015180235,37.560015298456165],[-122.38883528966466,37.551435083769555],[-122.44120000376466,37.46886044817503],[-122.37968502627967,37.39486668388987],[-122.2658032483195,37.4033911145053]]],[[[-122.27479015180235,37.560015298456165],[-122.2221915863209,37.64251820979243],[-122.28380352912619,37.71644156150208],[-122.39801249019256,37.707805739822945],[-122.45044537747678,37.625302208372474],[-122.38883528966466,37.551435083769555],[-122.27479015180235,37.560015298456165]]],[[[-122.44120000376466,37.46886044817503],[-122.38883528966466,37.551435083769555],[-122.45044537747678,37.625302208372474],[-122.56441735732743,37.61653827043709],[-122.61661591173977,37.53396360310495],[-122.55500895066726,37.46015287008624],[-122.44120000376466,37.46886044817503]]],[[[-122.45044537747678,37.625302208372474],[-122.39801249019256,37.707805739822945],[-122.4597179789842,37.781545330604914],[-122.57385345315238,37.77272521206777],[-122.62611963828853,37.69022171873274],[-122.56441735732743,37.61653827043709],[-122.45044537747678,37.625302208372474]]],[[[-122.28380352912619,37.71644156150208],[-122.23113607743653,37.79887236738518],[-122.29284349699644,37.872667939497596],[-122.4072167465147,37.863976689425115],[-122.4597179789842,37.781545330604914],[-122.39801249019256,37.707805739822945],[-122.28380352912619,37.71644156150208]]],[[[-122.28380352912619,37.71644156150208],[-122.23113607743653,37.79887236738518],[-122.29284349699644,37.872667939497596],[-122.4072167465147,37.863976689425115],[-122.4597179789842,37.781545330604914],[-122.39801249019256,37.707805739822945],[-122.28380352912619,37.71644156150208]]],[[[-122.28380352912619,37.71644156150208],[-122.23113607743653,37.79887236738518],[-122.29284349699644,37.872667939497596],[-122.4072167465147,37.863976689425115],[-122.4597179789842,37.781545330604914],[-122.39801249019256,37.707805739822945],[-122.28380352912619,37.71644156150208]]],[[[-122.28380352912619,37.71644156150208],[-122.23113607743653,37.79887236738518],[-122.29284349699644,37.872667939497596],[-122.4072167465147,37.863976689425115],[-122.4597179789842,37.781545330604914],[-122.39801249019256,37.707805739822945],[-122.28380352912619,37.71644156150208]]],[[[-122.28380352912619,37.71644156150208],[-122.23113607743653,37.79887236738518],[-122.29284349699644,37.872667939497596],[-122.4072167465147,37.863976689425115],[-122.4597179789842,37.781545330604914],[-122.39801249019256,37.707805739822945],[-122.28380352912619,37.71644156150208]]],[[[-122.28380352912619,37.71644156150208],[-122.23113607743653,37.79887236738518],[-122.29284349699644,37.872667939497596],[-122.4072167465147,37.863976689425115],[-122.4597179789842,37.781545330604914],[-122.39801249019256,37.707805739822945],[-122.28380352912619,37.71644156150208]]],[[[-122.28380352912619,37.71644156150208],[-122.23113607743653,37.79887236738518],[-122.29284349699644,37.872667939497596],[-122.4072167465147,37.863976689425115],[-122.4597179789842,37.781545330604914],[-122.39801249019256,37.707805739822945],[-122.28380352912619,37.71644156150208]]]]}} I can visualize them on geojson.io. They are correctly visualized there. However, when I try to run mask(hex, state); I am getting Each LinearRing of a Polygon must have 4 or more Positions. Which I do not understand.... Why can't it mask the polygon?
One cannot use two multipolygons with turf.mask(). The first argument of turf.mask() can be a polygon or multipolygon. But the second argument must be a polygon, not a multipolygon!
Quickly merge many contiguous polygons in javascript
Does anyone know of a way to merge thousands of polygons that are contiguous? I've been using turf's union function to do this in my prototypes but the time it takes grows to be way too slow as the list of polygons increases. I'm hoping/aiming for a solution that takes sub second time. Here is how I've been doing it. const turfUnion = require('#turf/union').default; const polygons = [ ... ]; const result = polygons.merge((m, f) => turfUnion(m, f)); As I mentioned this is too slow. It takes close to 5 minutes to merge 10,000 features. I'm guessing there is a way to do this much faster given that I know which polygons share a point with which other polygons and that all the polygons are contagious. The final result can have holes so the solution has to focus on interior perimeters as well as well as the external one. Any ideas or open source solutions would be great. Solutions in Javascript are preferred, but other low level languages would be OK. And here is a picture of one of large sets of polygons I'm looking to merge. The data for this can be found here. And here is the expected output.
Pairwise combine the polygons recursively The cost of computing the union of two polygons scales with the number of points in each. So you can reduce the runtime by reducing the number of operations that involve large polygons. The approach I take is to combine polygon1 with polygon2, then polygon3 with polygon4, all the way up to polygon(N-1) with polygonN. Then I repeat the process combining polygon1_2 (the union of polygons 1 and 2 from the previous step) with polygon3_4, all the way up to combining polygon(N-3)_(N-2) with polygon(N-1)_(N). Keep repeating this process until you only have one polygon remaining. Here's some sample code. It's python, not Javascript, but porting it shouldn't be difficult. def union(geom_list): """Rapidly combine a list of geometries into one. Inputs --------- geom_list A list of gdal/ogr geometries Returns ----------- A single geometry representing the union of the inputs """ if len(geom_list) == 1: return geom_list[0] if len(geom_list) == 2: return geom_list[0].Union(geom_list[1]) size = int(np.floor(len(geom_list)/2)) geom1 = union(geom_list[:size]) geom2 = union(geom_list[size:]) return geom1.Union(geom2) I can't claim this is the fastest possible way to do it, but it's much faster than adding one polygon at a time.
At the risk of sending you down a rabbit hole, here's what I would try if I was in your shoes... Stage 1: O(n). Consolidate all the line segments into an array, such that you end up with an array of line segments (ie, [x0,y0,x1,y1]) representing every polygon... [ [30.798218847530226, -26.663920391848013, 30.798209281734188, -26.66394228167575], [30.798209281734188, -26.66394228167575, 30.798201318284743, -26.663914720621534], [30.798201318284743, -26.663914720621534, 30.798218847530226, -26.663920391848013], ... ] Stage 2: O(n log n). Sort this entire array by x0, such that the line segments are now ordered according to the x value of the beginning of the segment. Stage 3: O(1). Beginning with the first element in the sorted array (segment 0), we can make the assumption that the segment with the leftmost x0 value has to be on the edge of the outer polygon. At this point we have segment 0's [x0,y0,x1,y1] as the starting outer edge segment. Stage 4: O(log n). Now, find the corrresponding line segments that begin with the end of the previous segment. In other words, which segments connect to the current segment? This should be less than a handful, typically one or two. Searching for the matching x0 is assumed to be binary, followed by a short localized linear search for all matching [x0,y0] combinations. Stage 5: O(1). If only one segment's [x0,y0] matched the last segment's [x1,y1], then add the segment to the list of outer edges. If more than one matching segment was found, then (assuming that we're moving in a clockwise direction) find the [x0,y0] pair that is furthest left of the current line segment, or if the outer edge is taking a right turn and none of the matching segments is to the left, then the [x0,y0] pair that is closest to the right of the current line segment. Add this new segment to the list of outer edges. (Note that there are matrix algorithms, which avoid the more expensive trig functions, to determine whether a point is to the left or right of a segment, in addition to the perpendicular distance from a point to a line / segment.) Stage 6: O(~n). Repeat Stage 4 until back at the starting outer edge segment. Overall algorithm should be O(n log n)... Note that this does not take into account interior perimeters, but believe that if you can determine a beginning segment that forms part of an interior perimeter and know whether the starting segment is moving clockwise or counterclockwise, then the same algorithm should work...
How to get L.polygon border, excluding inner points
I am working with Leaflet.js library. I have an array of geographical points, described by latitude and longitude and a polygon, based on this points. How can i remove inner points of polygon and draw only it's outer border? Array of points [[53, 31], [51.4, 31.2], [51.3, 32] ... etc.] //it's length ~ 500 points. Initializing map L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(mymap); Setting polygon L.polygon(points, {color: 'red'}).addTo(mymap); This is what i have right now. Here is all of 500 points is shown. Result i am expecting. Here all internal points are removed, showing the covered area.
Sounds like you are looking for a convex hull algorithm: it would provide you with the "envelope" of your Points. You can use e.g. Turfjs: https://turfjs.org/docs/#convex But note that it requires working with data converted to GeoJSON objects. You can also have a look at how Leaflet.markercluster plugin has implemented it.
Google map check if markers are shared with multiple polygons
I have a google map that has some markers.I have the coordinates or each marker and I have a drawing tool that allows me to draw polygons. I'm trying to detect if the markers inside a drawn polygon are shared with one or more others polygons so here is my logic and code : Logic : when I draw a polygon, I put it in an array called "polygons" then I remove the last drawn polygon from that array and I extract the locations of each marker inside it then for each marker extracted I check if it is inside an other polygon or not. Here is my code: The issue I have is that the variable shared_markers returns always true even if no markers are shared with 2 or more polygons. Any help please? Thanks
The line of code that is causing the problem is where you're getting all the polygons except the last one: function checkIfSharedMarkers(polygons, gmarkers){ ... var all_drawn_polygons_except_last_one = polygons.splice(-1,1); ... } The Array splice() method modifies the original array. I compared the length of the array before and after that line was called and it was the same - therefore all_drawn_polygons_except_last_one still contains the last drawn polygon. I replaced that line with: var all_drawn_polygons_except_last_one = polygons.slice(0, polygons.length-1) which uses Array slice() instead of splice() (this ensures the original polygons array is preserved). This returns the chosen elements in a new array so all_drawn_polygons_except_last_one now has the last element removed. Please see this Plunkr for a demo.
Group array of lat/lng coordinates together on map
I'm trying to compare an array of lat/lng coordinates for a map to see if any "cluster" or group together. I want to remove the ones that are too close together so if there are 4-5 stacking on top of each other on a map, it wil only show 1 until you zoom in a bit more, and then it will recalculate all of them again. I've tried comparing the array to itself, but it doesn't seem to give consistant results. Has anyone attempted something like this before? JSON Example: [ { Latitude = "44.033843"; Longitude = "-79.48865499999999"; }, { Latitude = "44.033843"; Longitude = "-79.48865499999999"; }]
Iterate the nodes and for a zoom level only display those that are beyond a set distance from each other. The haversine formula is simple enough to implement: example in JS here. http://www.movable-type.co.uk/scripts/latlong.html For the efficiency aspect, you probably don't want to calculate the entire list against the temporary list on every iteration so as a first level declutter a simple rounding works (every degree being ~60 miles from each other) ... start with rounding to the nearest 5 degrees, then 1, then 10ths, 100ths, etc as you zoom in. Ordering these lists and pulling unique array values first - then calculating distances from the resultant list. There are certainly many other algorithms to do it - but at some point you have to calculate distances.
EDIT: this assumes you're happy to fudge things a little bit, and instead of worrying about the actual distance between points on the globe, you look at the "Manhattan" distance of their lat/long coordinates. It depends on how precise you need to be, and whether you have points near the Earth's poles. But for most practical purposes this assumption should be fine. Suppose your desired precision is one decimal place. Then I would just iterate through the array, building up a has where the keys are the rounded coordinates and the values are arrays of lat/long pairs which round to the corresponding key. hash = Hash.new latLongArray.each { |point| key = [point.lat.round(1), point.long.round(1)] hash[key] = Array(hash[key]) + [point.lat, point.long] } This way you have them clustered, and you can in fact just put markers at the coordinates given by the keys themselves.