0

I'm trying to draw rects on a canvas for each element in my array. For the positioning I use the longitude and latitude values of the elements.

My array looks something like this and contains 50.000 elements that are objects:

var modified_array = 
[{"city":"NYC","longitude":-73.935242,"latitude":40.730610},
 {"city":"NYC","longitude":-74.044502,"latitude":40.689247}]

EDIT: The solution from @le_m helped me out a lot and I implemented a filter like he suggested:

const test2 = test.filter(({latitude, longitude}) => latitude != null 
              && longitude != null);

function getBoundingRect(test2) {
    let left = Infinity, right  = -Infinity;
    let top  = Infinity, bottom = -Infinity;

    for (let {latitude, longitude} of test2) {
        if (left   > latitude ) left   = latitude;
        if (top    > longitude) top    = longitude;
        if (right  < latitude) right  = latitude;
        if (bottom < longitude) bottom = longitude;
    }
    return {x: left, y: top, width: right - left, height: bottom - 
    top};
}

function draw(ctx, test2) {
    let boundingRect = getBoundingRect(test2);
    let scale = Math.min(canvas.width, canvas.height);

    for (let {latitude, longitude} of test2) {
        let x = (latitude  - boundingRect.x) / boundingRect.width  * 
        scale;
        let y = (longitude - boundingRect.y) / boundingRect.height * 
        scale;
        ctx.fillStyle = "rgba(65,105,225,0.2)";
        ctx.fillRect(x - 5, y - 5, 4, 4);
    }
}

draw(ctx, test2);

The filter doesn't seem to work What am I doing wrong?

Unknown User
  • 505
  • 1
  • 7
  • 19
  • Since your sample data's longitudes are negative, the point `lat*10,(lon+200)*2` is far off the canvas - or am I missing something? Do you actually have a transformation applied to the canvas? – le_m Jun 06 '17 at 21:49
  • @le_m No you're right. I corrected my example. I now see the elements but the scaling is what causes me problems right now. I don't have a clue how I could get all those coordinates displayed in a readable way since they are so close to each other. – Unknown User Jun 06 '17 at 21:56
  • You could compute the bounding rectangle of all points and project that rectangle to your canvas - i.e. stretch all points within the bounding rect to fill out the whole canvas. Could that be a solution for you? Also, do you want to apply some map projection e.g. https://stackoverflow.com/questions/14329691/covert-latitude-longitude-point-to-a-pixels-x-y-on-mercator-projection ? – le_m Jun 06 '17 at 22:02
  • @le_m Yes, tanks for that suggestion. Unfortunately I don't have a glimpse how I could code that? And no I don't want a mercator map projection. – Unknown User Jun 06 '17 at 22:14
  • @le_m You know tutorial on the web that show how to do this using JS? – Unknown User Jun 06 '17 at 22:30

2 Answers2

1

You may not be initializing your path. Try plugging in this code and tinkering with the values:

ctx.beginPath();
ctx.lineWidth="10";
ctx.strokeStyle="blue";
ctx.rect(50,50,150,80);
ctx.stroke();
1

I suggest computing the bounding box or bounding rectangle of all data points and stretch that bounding rectangle to fill your whole canvas:

function getBoundingRect(data) {
  let left = Infinity, right  = -Infinity;
  let top  = Infinity, bottom = -Infinity;
  
  for (let {latitude, longitude} of data) {
    if (left   > latitude ) left   = latitude;
    if (top    > longitude) top    = longitude;
    if (right  < latitude ) right  = latitude;
    if (bottom < longitude) bottom = longitude;
  }
  return {x: left, y: top, width: right - left, height: bottom - top};
}

function draw(ctx, data) {
  let boundingRect = getBoundingRect(data);
  let scale = Math.min(canvas.width, canvas.height);
  
  for (let {latitude, longitude} of data) {
    let x = (latitude  - boundingRect.x) / boundingRect.width  * scale;
    let y = (longitude - boundingRect.y) / boundingRect.height * scale;
    ctx.fillRect(x - 5, y - 5, 10, 10);
  }
}

let data = [
  {"city": "NYC", "longitude": -73.935242, "latitude": 40.730610},
  {"city": "NYC", "longitude": -74.044502, "latitude": 40.689247},
  {"city": "NYC", "longitude": -74.020219, "latitude": 40.578912},
  {"city": "NYC", "longitude": -73.992833, "latitude": 40.634345},
  {"city": "NYC", "longitude": -74.120332, "latitude": 40.484633}
];

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

draw(ctx, data);
<canvas id="canvas" width="200px" height="200px"></canvas>

The getBoundingRect(data) function computes the bounding rectangle - i.e. the smallest rectangle which still contains all given data points.

The bounding rectangle (left, top, right, bottom) is found by iterating over all data points and widening the rectangle whenever a point is discovered to lie outside the current bounding rectangle.

The draw function finally draws all data points on the given canvas context ctx. An offset (the bounding rectangle's left and top position) is subtracted from all data point coordinates. This guarantees that all data point coordinates are positive and greater than 0. Subsequently, the data point coordinates are scaled to stretch the whole canvas while maintaining the aspect ratio.

le_m
  • 19,302
  • 9
  • 64
  • 74
  • Thanks very much! Your code looks interesting and finally displays the elements in the array. What I don't understand is how I could stretch that bounding rectangle to fit the complete canvas? I multiplied the canvas width and height with a factor and that enlarges the map but how do I make sure I don't accidentally distort the map? Could you comment your code snippet? btw. it seems u are from ger like me:) unfortunately the ger community isn't that big here … – Unknown User Jun 07 '17 at 15:54
  • You want to keep the aspect ratio (i.e. stretch latitude by exactly the same factor as longitude)? Regarding the German community - I think many just don't bother adding their nationality to their profile... – le_m Jun 07 '17 at 15:58
  • I don't fully understand your getBoundingRect function. – Unknown User Jun 07 '17 at 16:03
  • @UnknownUser Let me know if something is still unclear. The left, right, top, bottom variables are initialized with +/-Infinity so that the if-conditions within the loop are all true for the first data point. – le_m Jun 07 '17 at 16:16
  • Your explanation in your answer is pretty good, thanks! While playing around with that snippet I noticed that with certain datasets the data points only get displayed very small in the left corner of my viewport … Could that have to do with the amount of data points? – Unknown User Jun 08 '17 at 21:51
  • @UnknownUser That's unusual. There should be at least one point either at the right or at the bottom. Otherwise it would indicate that the computed bounding rectangle or the coordinate transformation is incorrect. Can you share a dataset causing this behavior? – le_m Jun 08 '17 at 22:02
  • I guess you mean the data points should stretch to the full width and height of the viewport? Unfortunately I can't give you the original dataset on which I noticed that behavior since its a private dataset, but I found a free dataset from where a similar thing happens: The datapoints are all squashed together on a small spot at the top of my viewport. https://data.cityofnewyork.us/Environment/Public-Recycling-Bins/sxx4-xhzg This dataset kind of reflects my problem since my data points are also located very close to each other in just one city. – Unknown User Jun 09 '17 at 19:50
  • @UnknownUser The dataset you linked contains elements such as `[ 335, "209CC40A-587C-435D-8EF1-A5F0BC545DC8", 335, 1340977873, "392904", 1340977873, "392904", "{\n}", "Manhattan", "Outdoor", "Pier 25", "Hudson River Park ", null, null ]` whose latitude and longitude are `null`, which is converted to a numeric `0`. You need to filter such values e.g. by `data = data.filter(({latitude, longitude}) => latitude != null && longitude != null)`. – le_m Jun 09 '17 at 21:23
  • Yes that absolutely makes sense. I implemented the filter as shown in my post but it doesn't work. What am I doing wrong? – Unknown User Jun 10 '17 at 13:33
  • @UnknownUser Sure that you don't have any other issue with your data? What's the result of `data.sort((a,b) => a.latitude - b.latitude)[0]` - anything strange? – le_m Jun 10 '17 at 21:30
  • Nothing strange as far as I can see it. Maybe I need somebody to take a look at this. I will then edit my post if anything important is found. Until then thank you very much for your help! I'm incredible thankful for it! – Unknown User Jun 10 '17 at 21:47