Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- using System.Drawing;
- namespace GeoCodePlotter
- {
- class Program
- {
- static void Main(string[] args)
- {
- new Plotter().Run();
- }
- }
- }
- namespace GeoCodePlotter
- {
- class Plotter
- {
- public void Run()
- {
- LoadPostcodes();
- LoadMap();
- LoadReferencePoints();
- Plot();
- Save();
- }
- Dictionary<int, double> PostcodeLatitude;
- Dictionary<int, double> PostcodeLongitude;
- /// <summary>
- /// Loads the postcodes and their longitude/latitude from PostcodeLatLng.csv
- /// </summary>
- void LoadPostcodes()
- {
- PostcodeLatitude = new Dictionary<int, double>();
- PostcodeLongitude = new Dictionary<int, double>();
- foreach (string line in File.ReadAllLines("PostcodeLatLng.csv"))
- {
- string[] csv = line.Split(',');
- if (csv.Length >= 3)
- {
- int postcode;
- double latitude, longitude;
- // try to parse the values, if they're invalid then just 'continue' to the next line in the file
- if (!int.TryParse(csv[0], out postcode)) continue;
- if (!double.TryParse(csv[1], out latitude)) continue;
- if (!double.TryParse(csv[2], out longitude)) continue;
- // save the values to the dictionaries
- PostcodeLatitude[postcode] = latitude;
- PostcodeLongitude[postcode] = longitude;
- }
- }
- }
- Bitmap Map;
- /// <summary>
- /// Loads the png map
- /// </summary>
- void LoadMap()
- {
- Map = new Bitmap("Map.png");
- }
- List<ReferencePoint> ReferencePointsX; // X ~= longitude
- List<ReferencePoint> ReferencePointsY; // Y ~= latitude
- /// <summary>
- /// Loads the reference points from Map.txt - these points reference postcodes to x,y coordinates on the map
- /// </summary>
- void LoadReferencePoints()
- {
- ReferencePointsX = new List<ReferencePoint>();
- ReferencePointsY = new List<ReferencePoint>();
- foreach (string line in File.ReadAllLines("Map.txt"))
- {
- string[] csv = line.Split(',');
- if (csv.Length >= 4)
- {
- int postcode, x, y;
- // try to parse the values, if they're invalid then just 'continue' to the next line in the file
- if (!int.TryParse(csv[0], out postcode)) continue;
- if (!int.TryParse(csv[2], out x)) continue;
- if (!int.TryParse(csv[3], out y)) continue;
- // Does this postcode exist?
- if (PostcodeLatitude.ContainsKey(postcode))
- {
- // Add the two reference points
- ReferencePointsX.Add(new ReferencePoint(PostcodeLongitude[postcode], x, postcode));
- ReferencePointsY.Add(new ReferencePoint(PostcodeLatitude[postcode], y, postcode));
- }
- }
- }
- }
- /// <summary>
- /// Save the resultant plotted image out
- /// </summary>
- void Save()
- {
- Map.Save("OutputMap.png");
- }
- /// <summary>
- /// Choose which plotting method we want
- /// </summary>
- void Plot()
- {
- //PlotHeatmap();
- PlotBlobs();
- }
- /// <summary>
- /// Plot the points from PlotData.csv
- /// This version does blobs in each spot there's a store or whatever
- /// </summary>
- void PlotBlobs()
- {
- string[] lines = File.ReadAllLines("PlotData.csv");
- int i = 0;
- foreach (string line in lines)
- {
- string[] csv = line.Split(',');
- if (csv.Length >= 2)
- {
- int postcode, colour;
- // try to parse the values, if they're invalid then just 'continue' to the next line in the file
- if (!int.TryParse(csv[0], out postcode)) continue;
- colour = Convert.ToInt32(csv[1], 16); // parse hex
- // plot it
- PlotSinglePoint(postcode, colour);
- }
- // Let the user know where its up to
- Console.Write("\r{0} %", 100 * i / lines.Length);
- i++;
- }
- Console.WriteLine("\rDone ");
- }
- /// <summary>
- /// Loads PlotData.csv
- /// This version plots a heatmap of distances to the nearest store or whatever
- /// </summary>
- void PlotHeatmap()
- {
- // Make a list of x,y points of all the stores
- List<int> xpoints = new List<int>();
- List<int> ypoints = new List<int>();
- foreach (string line in File.ReadAllLines("PlotData.csv"))
- {
- // try to parse the postcode, if they're invalid then just 'continue' to the next line in the file
- int postcode;
- if (!int.TryParse(line.Split(',')[0], out postcode)) continue;
- // Does this postcode exist?
- if (PostcodeLatitude.ContainsKey(postcode))
- {
- double latitude = PostcodeLatitude[postcode];
- double longitude = PostcodeLongitude[postcode];
- // Convert to x/y
- int x, y;
- LatLngToXY(latitude, longitude, out x, out y, postcode);
- // Is it on-map?
- if (x > 0 && y > 0 && x < Map.Width && y < Map.Height)
- {
- // Add it to the list
- xpoints.Add(x);
- ypoints.Add(y);
- }
- }
- }
- // Now make the heatmap
- for (int y = 0; y < Map.Height; y += 2)
- {
- for (int x = 0; x < Map.Width; x += 2)
- {
- // Find the distance in pixels to the nearest store/whatever
- double dist = 360;
- for (int i = 0; i < xpoints.Count; i++)
- dist = Math.Min(dist, Math.Sqrt((xpoints[i] - x) * (xpoints[i] - x) + (ypoints[i] - y) * (ypoints[i] - y)));
- // Plot it
- int r = 0, g = 255, b = 0; // Outside=red
- //if (dist < 200) { r = 255; g = 128; b = 0; } // Within 100km=orange
- //if (dist < 100) { r = 0; g = 0; b = 255; } // Within 50km=blue
- //if (dist < 40) { r = 0; g = 255; b = 0; } // Within 20km=green
- HsvToRgb(110-dist, 1, 1, out r, out g, out b);
- // Red = bad, no stores anywhere near
- // Green = good, stores very close
- //int r = Clamp((int)(dist * 2.55));
- PutPixel(x, y, r, g, b, 50);
- PutPixel(x + 1, y, r, g, b, 50);
- PutPixel(x, y + 1, r, g, b, 50);
- PutPixel(x + 1, y + 1, r, g, b, 50);
- }
- Console.Write("\r{0} %", 100 * y / Map.Height);
- }
- Console.Write("\rDone ");
- }
- /// <summary>
- /// Convert HSV to RGB
- /// h is from 0-360
- /// s,v values are 0-1
- /// r,g,b values are 0-255
- /// </summary>
- void HsvToRgb(double h, double S, double V, out int r, out int g, out int b)
- {
- // Based upon http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space#HSV_Transformation_C_.2F_C.2B.2B_Code_2
- // H is from 0-360
- // S, V are 0-1
- // ######################################################################
- // T. Nathan Mundhenk
- // C/C++ Macro HSV to RGB
- double H = h;
- while (H < 0) { H += 360; };
- while (H >= 360) { H -= 360; };
- double R, G, B;
- if (V == 0)
- { R = G = B = 0; }
- else if (S == 0)
- {
- R = G = B = V;
- }
- else
- {
- double hf = H / 60.0;
- int i = (int)Math.Floor(hf);
- double f = hf - i;
- double pv = V * (1 - S);
- double qv = V * (1 - S * f);
- double tv = V * (1 - S * (1 - f));
- switch (i)
- {
- // Red is the dominant color
- case 0:
- R = V;
- G = tv;
- B = pv;
- break;
- // Green is the dominant color
- case 1:
- R = qv;
- G = V;
- B = pv;
- break;
- case 2:
- R = pv;
- G = V;
- B = tv;
- break;
- // Blue is the dominant color
- case 3:
- R = pv;
- G = qv;
- B = V;
- break;
- case 4:
- R = tv;
- G = pv;
- B = V;
- break;
- // Red is the dominant color
- case 5:
- R = V;
- G = pv;
- B = qv;
- break;
- // Just in case we overshoot on our math by a little, we put these here. Since its a switch it won't slow us down at all to put these here.
- case 6:
- R = V;
- G = tv;
- B = pv;
- break;
- case -1:
- R = V;
- G = pv;
- B = qv;
- break;
- // The color is not defined, we should throw an error.
- default:
- //LFATAL("i Value error in Pixel conversion, Value is %d", i);
- R = G = B = V; // Just pretend its black/white
- break;
- }
- }
- r = Clamp((int)(R * 255.0));
- g = Clamp((int)(G * 255.0));
- b = Clamp((int)(B * 255.0));
- }
- /// <summary>
- /// Plots a single point on the map if it can
- /// </summary>
- void PlotSinglePoint(int postcode, int colour)
- {
- // Does this postcode exist?
- if (PostcodeLatitude.ContainsKey(postcode))
- {
- // look up the lat/lng
- double latitude = PostcodeLatitude[postcode];
- double longitude = PostcodeLongitude[postcode];
- // Convert to x/y
- int x, y;
- LatLngToXY(latitude, longitude, out x, out y, postcode);
- // Plot it
- PutBlob(x, y, colour);
- }
- }
- /// <summary>
- /// Puts a blob
- /// </summary>
- void PutBlob(int x, int y, int colour)
- {
- int red = (colour & 0xff0000) >> 16;
- int grn = (colour & 0xff00) >> 8;
- int blu = colour & 0xff;
- const int radius = 20;
- for (int x2 = x - radius; x2 <= x + radius; x2++)
- for (int y2 = y - radius; y2 <= y + radius; y2++)
- {
- double distance = Math.Sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y));
- int alpha = Clamp(255 - (int)(distance * 255 / radius)) / 2; // spherical blob
- //int alpha = Clamp((int)(.9 / (distance / radius + 0.1))*5); // inverse blob
- PutPixel(x2, y2, red, grn, blu, alpha);
- }
- /*
- PutPixel(x, y, red, grn, blu, 255);
- PutPixel(x + 1, y, red, grn, blu, 128);
- PutPixel(x - 1, y, red, grn, blu, 128);
- PutPixel(x, y + 1, red, grn, blu, 128);
- PutPixel(x, y - 1, red, grn, blu, 128);
- PutPixel(x + 1, y + 1, red, grn, blu, 64);
- PutPixel(x - 1, y + 1, red, grn, blu, 64);
- PutPixel(x + 1, y - 1, red, grn, blu, 64);
- PutPixel(x - 1, y - 1, red, grn, blu, 64);
- PutPixel(x + 2, y, red, grn, blu, 32);
- PutPixel(x - 2, y, red, grn, blu, 32);
- PutPixel(x, y + 2, red, grn, blu, 32);
- PutPixel(x, y - 2, red, grn, blu, 32);
- */
- }
- /// <summary>
- /// Puts a pixel at the given coords.
- /// RGBA are in the range 0-255
- /// </summary>
- void PutPixel(int x, int y, int r, int g, int b, int alpha)
- {
- if (x >= 0 && y >= 0 && x < Map.Width && y < Map.Height)
- {
- Color original = Map.GetPixel(x, y);
- /* // Normal mixing
- Color newcolour = Color.FromArgb(
- Clamp(original.R + (r - original.R) * alpha / 255),
- Clamp(original.G + (g - original.G) * alpha / 255),
- Clamp(original.B + (b - original.B) * alpha / 255));
- */
- // Additive mixing
- Color newcolour = Color.FromArgb(
- Clamp(original.R + r * alpha / 255),
- Clamp(original.G + g * alpha / 255),
- Clamp(original.B + b * alpha / 255));
- Map.SetPixel(x, y, newcolour);
- }
- }
- /// <summary>
- /// Clamp a value to 0-255
- /// </summary>
- int Clamp(int i)
- {
- if (i < 0) return 0;
- if (i > 255) return 255;
- return i;
- }
- /// <summary>
- /// Converts a longitude/latitude to x/y
- /// </summary>
- void LatLngToXY(double latitude, double longitude, out int x, out int y, int postcode)
- {
- // Firstly go through the list and find the nearest 2 points
- ReferencePoint x1 = null, x2 = null, y1 = null, y2 = null;
- foreach (ReferencePoint p in ReferencePointsX)
- if (x1 == null || Math.Abs(longitude - p.LongOrLat) < Math.Abs(longitude - x1.LongOrLat)) x1 = p;
- foreach (ReferencePoint p in ReferencePointsX)
- if (p != x1)
- if (x2 == null || Math.Abs(longitude - p.LongOrLat) < Math.Abs(longitude - x2.LongOrLat)) x2 = p;
- foreach (ReferencePoint p in ReferencePointsY)
- if (y1 == null || Math.Abs(latitude - p.LongOrLat) < Math.Abs(latitude - y1.LongOrLat)) y1 = p;
- foreach (ReferencePoint p in ReferencePointsY)
- if (p != y1)
- if (y2 == null || Math.Abs(latitude - p.LongOrLat) < Math.Abs(latitude - y2.LongOrLat)) y2 = p;
- // Now interpolate between them
- x = (int)(
- (longitude - x1.LongOrLat) / (x2.LongOrLat - x1.LongOrLat) *
- (x2.XOrY - x1.XOrY) + x1.XOrY
- );
- y = (int)(
- (latitude - y1.LongOrLat) / (y2.LongOrLat - y1.LongOrLat) *
- (y2.XOrY - y1.XOrY) + y1.XOrY
- );
- }
- }
- /// <summary>
- /// This is a single reference point that maps a long/latitude to a x/y coordinate on the map
- /// </summary>
- class ReferencePoint
- {
- public double LongOrLat, XOrY;
- public int postcode;
- public ReferencePoint(double _LongOrLat, double _XOrY, int _postcode)
- {
- LongOrLat = _LongOrLat;
- XOrY = _XOrY;
- postcode = _postcode;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement