I'm creating a graphic of the glow of lights above a geographic location based upon Walkers Law: Skyglow=0.01*Population*DistanceFromCenter^-2.5
I have a CSV file of places with 66,000 records using 5 fields (id,name,population,latitude,longitude), parsed on the FormLoad event and stored it in:
List<string[]> placeDataList
Then I set up nested loops to fill in a bitmap using SetPixel. For each pixel on the bitmap, which represents a coordinate on a map (latitude and longitude), the program loops through placeDataList – calculating the distance from that coordinate (pixel) to each place record. The distance (along with population) is used in a calculation to find how much cumulative sky glow is contributed to the coordinate from each place record.
So, for every pixel, 66,000 distance calculations must be made. The problem is, this is predictably EXTREMELY slow – on the order of one line of pixels per 30 seconds or so on a 320 pixel wide image. This is unrelated to SetPixel, which I know is also slow, because the speed is similarly slow when adding the distance calculation results to an array.
I don’t actually need to test all 66,000 records for every pixel, only the records within 150 miles (i.e. no skyglow is contributed to a coordinate from a small town 3000 miles away). But to find which records are within 150 miles of my coordinate I would still need to loop through all the records for each pixel. I can't use a smaller number of records because all 66,000 places contribute to skyglow for SOME coordinate in my map as it loops. This seems like a Catch-22, so I know there must be a better method out there. Like I mentioned, the slowdown is related to how many calculations I’m making per pixel, not anything to do with the bitmap. Any suggestions?
private void fillPixels(int width)
{
Color pixelColor;
int pixel_w = width;
int pixel_h = (int)Math.Floor((width * 0.424088664));
Bitmap bmp = new Bitmap(pixel_w, pixel_h);
for (int i = 0; i < pixel_h; i++)
for (int j = 0; j < pixel_w; j++)
{
pixelColor = getPixelColor(i, j);
bmp.SetPixel(j, i, pixelColor);
}
bmp.Save("Nightfall", System.Drawing.Imaging.ImageFormat.Jpeg);
pictureBox1.Image = bmp;
MessageBox.Show("Done");
}
private Color getPixelColor(int height, int width)
{
int c;
double glow,d,cityLat,cityLon,cityPop;
double testLat, testLon;
int size_h = (int)Math.Floor((size_w * 0.424088664)); ;
testLat = (height * (24.443136 / size_h)) + 24.548874;
testLon = (width * (57.636853 / size_w)) -124.640767;
glow = 0;
for (int i = 0; i < placeDataList.Count; i++)
{
cityPop=Convert.ToDouble(placeDataList[i][2]);
cityLat=Convert.ToDouble(placeDataList[i][3]);
cityLon=Convert.ToDouble(placeDataList[i][4]);
d = distance(testLat, testLon, cityLat, cityLon,"M");
if(d<150)
glow = glow+(0.01 * cityPop * Math.Pow(d, -2.5));
}
if (glow >= 1) glow=1;
c = (int)Math.Ceiling(glow * 255);
return Color.FromArgb(c, c, c);
}