Advertisement
Guest User

Chris

a guest
Oct 23rd, 2008
775
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.61 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.IO;
  5. using System.Drawing;
  6.  
  7. namespace GeoCodePlotter
  8. {
  9.   class Program
  10.   {
  11.     static void Main(string[] args)
  12.     {
  13.       new Plotter().Run();
  14.     }
  15.   }
  16. }
  17.  
  18. namespace GeoCodePlotter
  19. {
  20.   class Plotter
  21.   {
  22.     public void Run()
  23.     {
  24.       LoadPostcodes();
  25.       LoadMap();
  26.       LoadReferencePoints();
  27.       Plot();
  28.       Save();
  29.     }
  30.  
  31.  
  32.     Dictionary<int, double> PostcodeLatitude;
  33.     Dictionary<int, double> PostcodeLongitude;
  34.  
  35.     /// <summary>
  36.     /// Loads the postcodes and their longitude/latitude from PostcodeLatLng.csv
  37.     /// </summary>
  38.     void LoadPostcodes()
  39.     {
  40.       PostcodeLatitude = new Dictionary<int, double>();
  41.       PostcodeLongitude = new Dictionary<int, double>();
  42.       foreach (string line in File.ReadAllLines("PostcodeLatLng.csv"))
  43.       {
  44.         string[] csv = line.Split(',');
  45.         if (csv.Length >= 3)
  46.         {
  47.           int postcode;
  48.           double latitude, longitude;
  49.  
  50.           // try to parse the values, if they're invalid then just 'continue' to the next line in the file
  51.           if (!int.TryParse(csv[0], out postcode)) continue;
  52.           if (!double.TryParse(csv[1], out latitude)) continue;
  53.           if (!double.TryParse(csv[2], out longitude)) continue;
  54.  
  55.           // save the values to the dictionaries
  56.           PostcodeLatitude[postcode] = latitude;
  57.           PostcodeLongitude[postcode] = longitude;
  58.         }
  59.       }
  60.     }
  61.  
  62.     Bitmap Map;
  63.  
  64.     /// <summary>
  65.     /// Loads the png map
  66.     /// </summary>
  67.     void LoadMap()
  68.     {
  69.       Map = new Bitmap("Map.png");
  70.     }
  71.  
  72.     List<ReferencePoint> ReferencePointsX; // X ~= longitude
  73.     List<ReferencePoint> ReferencePointsY; // Y ~= latitude
  74.  
  75.     /// <summary>
  76.     /// Loads the reference points from Map.txt - these points reference postcodes to x,y coordinates on the map
  77.     /// </summary>
  78.     void LoadReferencePoints()
  79.     {
  80.       ReferencePointsX = new List<ReferencePoint>();
  81.       ReferencePointsY = new List<ReferencePoint>();
  82.       foreach (string line in File.ReadAllLines("Map.txt"))
  83.       {
  84.         string[] csv = line.Split(',');
  85.         if (csv.Length >= 4)
  86.         {
  87.           int postcode, x, y;
  88.  
  89.           // try to parse the values, if they're invalid then just 'continue' to the next line in the file
  90.           if (!int.TryParse(csv[0], out postcode)) continue;
  91.           if (!int.TryParse(csv[2], out x)) continue;
  92.           if (!int.TryParse(csv[3], out y)) continue;
  93.  
  94.           // Does this postcode exist?
  95.           if (PostcodeLatitude.ContainsKey(postcode))
  96.           {
  97.             // Add the two reference points
  98.             ReferencePointsX.Add(new ReferencePoint(PostcodeLongitude[postcode], x, postcode));
  99.             ReferencePointsY.Add(new ReferencePoint(PostcodeLatitude[postcode], y, postcode));
  100.           }
  101.         }
  102.       }
  103.     }
  104.  
  105.     /// <summary>
  106.     /// Save the resultant plotted image out
  107.     /// </summary>
  108.     void Save()
  109.     {
  110.       Map.Save("OutputMap.png");
  111.     }
  112.  
  113.     /// <summary>
  114.     /// Choose which plotting method we want
  115.     /// </summary>
  116.     void Plot()
  117.     {
  118.       //PlotHeatmap();
  119.       PlotBlobs();
  120.     }
  121.  
  122.     /// <summary>
  123.     /// Plot the points from PlotData.csv
  124.     /// This version does blobs in each spot there's a store or whatever
  125.     /// </summary>
  126.     void PlotBlobs()
  127.     {
  128.       string[] lines = File.ReadAllLines("PlotData.csv");
  129.       int i = 0;
  130.       foreach (string line in lines)
  131.       {
  132.         string[] csv = line.Split(',');
  133.         if (csv.Length >= 2)
  134.         {
  135.           int postcode, colour;
  136.  
  137.           // try to parse the values, if they're invalid then just 'continue' to the next line in the file
  138.           if (!int.TryParse(csv[0], out postcode)) continue;
  139.           colour = Convert.ToInt32(csv[1], 16); // parse hex
  140.  
  141.           // plot it
  142.           PlotSinglePoint(postcode, colour);
  143.         }
  144.  
  145.         // Let the user know where its up to
  146.         Console.Write("\r{0} %", 100 * i / lines.Length);
  147.         i++;
  148.       }
  149.       Console.WriteLine("\rDone   ");
  150.     }
  151.  
  152.     /// <summary>
  153.     /// Loads PlotData.csv
  154.     /// This version plots a heatmap of distances to the nearest store or whatever
  155.     /// </summary>
  156.     void PlotHeatmap()
  157.     {
  158.       // Make a list of x,y points of all the stores
  159.       List<int> xpoints = new List<int>();
  160.       List<int> ypoints = new List<int>();
  161.       foreach (string line in File.ReadAllLines("PlotData.csv"))
  162.       {
  163.         // try to parse the postcode, if they're invalid then just 'continue' to the next line in the file
  164.         int postcode;
  165.         if (!int.TryParse(line.Split(',')[0], out postcode)) continue;
  166.  
  167.         // Does this postcode exist?
  168.         if (PostcodeLatitude.ContainsKey(postcode))
  169.         {
  170.           double latitude = PostcodeLatitude[postcode];
  171.           double longitude = PostcodeLongitude[postcode];
  172.           // Convert to x/y
  173.           int x, y;
  174.           LatLngToXY(latitude, longitude, out x, out y, postcode);
  175.           // Is it on-map?
  176.           if (x > 0 && y > 0 && x < Map.Width && y < Map.Height)
  177.           {
  178.             // Add it to the list
  179.             xpoints.Add(x);
  180.             ypoints.Add(y);
  181.           }
  182.         }
  183.       }
  184.  
  185.       // Now make the heatmap
  186.       for (int y = 0; y < Map.Height; y += 2)
  187.       {
  188.         for (int x = 0; x < Map.Width; x += 2)
  189.         {
  190.           // Find the distance in pixels to the nearest store/whatever
  191.           double dist = 360;
  192.           for (int i = 0; i < xpoints.Count; i++)
  193.             dist = Math.Min(dist, Math.Sqrt((xpoints[i] - x) * (xpoints[i] - x) + (ypoints[i] - y) * (ypoints[i] - y)));
  194.  
  195.           // Plot it
  196.  
  197.           int r = 0, g = 255, b = 0; // Outside=red
  198.           //if (dist < 200) { r = 255; g = 128; b = 0; } // Within 100km=orange
  199.           //if (dist < 100) { r = 0; g = 0; b = 255; } // Within 50km=blue
  200.           //if (dist < 40) { r = 0; g = 255; b = 0; } // Within 20km=green
  201.  
  202.           HsvToRgb(110-dist, 1, 1, out r, out g, out b);
  203.  
  204.           // Red = bad, no stores anywhere near
  205.           // Green = good, stores very close
  206.           //int r = Clamp((int)(dist * 2.55));
  207.  
  208.           PutPixel(x, y, r, g, b, 50);
  209.           PutPixel(x + 1, y, r, g, b, 50);
  210.           PutPixel(x, y + 1, r, g, b, 50);
  211.           PutPixel(x + 1, y + 1, r, g, b, 50);
  212.         }
  213.         Console.Write("\r{0} %", 100 * y / Map.Height);
  214.       }
  215.       Console.Write("\rDone   ");
  216.     }
  217.  
  218.     /// <summary>
  219.     /// Convert HSV to RGB
  220.     /// h is from 0-360
  221.     /// s,v values are 0-1
  222.     /// r,g,b values are 0-255
  223.     /// </summary>
  224.     void HsvToRgb(double h, double S, double V, out int r, out int g, out int b)
  225.     {
  226.       // Based upon http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space#HSV_Transformation_C_.2F_C.2B.2B_Code_2
  227.       // H is from 0-360
  228.       // S, V are 0-1
  229.       // ######################################################################
  230.       // T. Nathan Mundhenk
  231.       // [email protected]
  232.       // C/C++ Macro HSV to RGB
  233.  
  234.       double H = h;
  235.       while (H < 0) { H += 360; };
  236.       while (H >= 360) { H -= 360; };
  237.       double R, G, B;
  238.       if (V == 0)
  239.         { R = G = B = 0; }
  240.       else if (S == 0)
  241.       {
  242.         R = G = B = V;
  243.       }
  244.       else
  245.       {
  246.         double hf = H / 60.0;
  247.         int i = (int)Math.Floor(hf);
  248.         double f = hf - i;
  249.         double pv = V * (1 - S);
  250.         double qv = V * (1 - S * f);
  251.         double tv = V * (1 - S * (1 - f));
  252.         switch (i)
  253.         {
  254.  
  255.           // Red is the dominant color
  256.  
  257.           case 0:
  258.             R = V;
  259.             G = tv;
  260.             B = pv;
  261.             break;
  262.  
  263.           // Green is the dominant color
  264.  
  265.           case 1:
  266.             R = qv;
  267.             G = V;
  268.             B = pv;
  269.             break;
  270.           case 2:
  271.             R = pv;
  272.             G = V;
  273.             B = tv;
  274.             break;
  275.  
  276.           // Blue is the dominant color
  277.  
  278.           case 3:
  279.             R = pv;
  280.             G = qv;
  281.             B = V;
  282.             break;
  283.           case 4:
  284.             R = tv;
  285.             G = pv;
  286.             B = V;
  287.             break;
  288.  
  289.           // Red is the dominant color
  290.  
  291.           case 5:
  292.             R = V;
  293.             G = pv;
  294.             B = qv;
  295.             break;
  296.  
  297.           // 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.
  298.  
  299.           case 6:
  300.             R = V;
  301.             G = tv;
  302.             B = pv;
  303.             break;
  304.           case -1:
  305.             R = V;
  306.             G = pv;
  307.             B = qv;
  308.             break;
  309.  
  310.           // The color is not defined, we should throw an error.
  311.  
  312.           default:
  313.             //LFATAL("i Value error in Pixel conversion, Value is %d", i);
  314.             R = G = B = V; // Just pretend its black/white
  315.             break;
  316.         }
  317.       }
  318.       r = Clamp((int)(R * 255.0));
  319.       g = Clamp((int)(G * 255.0));
  320.       b = Clamp((int)(B * 255.0));
  321.     }
  322.  
  323.     /// <summary>
  324.     /// Plots a single point on the map if it can
  325.     /// </summary>
  326.     void PlotSinglePoint(int postcode, int colour)
  327.     {
  328.       // Does this postcode exist?
  329.       if (PostcodeLatitude.ContainsKey(postcode))
  330.       {
  331.         // look up the lat/lng
  332.         double latitude = PostcodeLatitude[postcode];
  333.         double longitude = PostcodeLongitude[postcode];
  334.  
  335.         // Convert to x/y
  336.         int x, y;
  337.         LatLngToXY(latitude, longitude, out x, out y, postcode);
  338.  
  339.         // Plot it
  340.         PutBlob(x, y, colour);
  341.       }
  342.     }
  343.  
  344.     /// <summary>
  345.     /// Puts a blob
  346.     /// </summary>
  347.     void PutBlob(int x, int y, int colour)
  348.     {
  349.       int red = (colour & 0xff0000) >> 16;
  350.       int grn = (colour & 0xff00) >> 8;
  351.       int blu = colour & 0xff;
  352.       const int radius = 20;
  353.  
  354.       for (int x2 = x - radius; x2 <= x + radius; x2++)
  355.         for (int y2 = y - radius; y2 <= y + radius; y2++)
  356.         {
  357.           double distance = Math.Sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y));
  358.           int alpha = Clamp(255 - (int)(distance * 255 / radius)) / 2; // spherical blob
  359.           //int alpha = Clamp((int)(.9 / (distance / radius + 0.1))*5); // inverse blob
  360.           PutPixel(x2, y2, red, grn, blu, alpha);
  361.         }
  362.       /*
  363.       PutPixel(x, y, red, grn, blu, 255);
  364.       PutPixel(x + 1, y, red, grn, blu, 128);
  365.       PutPixel(x - 1, y, red, grn, blu, 128);
  366.       PutPixel(x, y + 1, red, grn, blu, 128);
  367.       PutPixel(x, y - 1, red, grn, blu, 128);
  368.       PutPixel(x + 1, y + 1, red, grn, blu, 64);
  369.       PutPixel(x - 1, y + 1, red, grn, blu, 64);
  370.       PutPixel(x + 1, y - 1, red, grn, blu, 64);
  371.       PutPixel(x - 1, y - 1, red, grn, blu, 64);
  372.       PutPixel(x + 2, y, red, grn, blu, 32);
  373.       PutPixel(x - 2, y, red, grn, blu, 32);
  374.       PutPixel(x, y + 2, red, grn, blu, 32);
  375.       PutPixel(x, y - 2, red, grn, blu, 32);
  376.       */
  377.     }
  378.  
  379.     /// <summary>
  380.     /// Puts a pixel at the given coords.
  381.     /// RGBA are in the range 0-255
  382.     /// </summary>
  383.     void PutPixel(int x, int y, int r, int g, int b, int alpha)
  384.     {
  385.       if (x >= 0 && y >= 0 && x < Map.Width && y < Map.Height)
  386.       {
  387.         Color original = Map.GetPixel(x, y);
  388.  
  389.         /* // Normal mixing
  390.         Color newcolour = Color.FromArgb(
  391.           Clamp(original.R + (r - original.R) * alpha / 255),
  392.           Clamp(original.G + (g - original.G) * alpha / 255),
  393.           Clamp(original.B + (b - original.B) * alpha / 255));
  394.         */
  395.  
  396.         // Additive mixing
  397.         Color newcolour = Color.FromArgb(
  398.           Clamp(original.R + r * alpha / 255),
  399.           Clamp(original.G + g * alpha / 255),
  400.           Clamp(original.B + b * alpha / 255));
  401.  
  402.         Map.SetPixel(x, y, newcolour);
  403.       }
  404.     }
  405.  
  406.     /// <summary>
  407.     /// Clamp a value to 0-255
  408.     /// </summary>
  409.     int Clamp(int i)
  410.     {
  411.       if (i < 0) return 0;
  412.       if (i > 255) return 255;
  413.       return i;
  414.     }
  415.  
  416.     /// <summary>
  417.     /// Converts a longitude/latitude to x/y
  418.     /// </summary>
  419.     void LatLngToXY(double latitude, double longitude, out int x, out int y, int postcode)
  420.     {
  421.       // Firstly go through the list and find the nearest 2 points
  422.       ReferencePoint x1 = null, x2 = null, y1 = null, y2 = null;
  423.  
  424.       foreach (ReferencePoint p in ReferencePointsX)
  425.         if (x1 == null || Math.Abs(longitude - p.LongOrLat) < Math.Abs(longitude - x1.LongOrLat)) x1 = p;
  426.  
  427.       foreach (ReferencePoint p in ReferencePointsX)
  428.         if (p != x1)
  429.           if (x2 == null || Math.Abs(longitude - p.LongOrLat) < Math.Abs(longitude - x2.LongOrLat)) x2 = p;
  430.  
  431.       foreach (ReferencePoint p in ReferencePointsY)
  432.         if (y1 == null || Math.Abs(latitude - p.LongOrLat) < Math.Abs(latitude - y1.LongOrLat)) y1 = p;
  433.  
  434.       foreach (ReferencePoint p in ReferencePointsY)
  435.         if (p != y1)
  436.           if (y2 == null || Math.Abs(latitude - p.LongOrLat) < Math.Abs(latitude - y2.LongOrLat)) y2 = p;
  437.  
  438.       // Now interpolate between them
  439.       x = (int)(
  440.         (longitude - x1.LongOrLat) / (x2.LongOrLat - x1.LongOrLat) *
  441.         (x2.XOrY - x1.XOrY) + x1.XOrY
  442.       );
  443.       y = (int)(
  444.         (latitude - y1.LongOrLat) / (y2.LongOrLat - y1.LongOrLat) *
  445.         (y2.XOrY - y1.XOrY) + y1.XOrY
  446.       );
  447.     }
  448.   }
  449.  
  450.   /// <summary>
  451.   /// This is a single reference point that maps a long/latitude to a x/y coordinate on the map
  452.   /// </summary>
  453.   class ReferencePoint
  454.   {
  455.     public double LongOrLat, XOrY;
  456.     public int postcode;
  457.     public ReferencePoint(double _LongOrLat, double _XOrY, int _postcode)
  458.     {
  459.       LongOrLat = _LongOrLat;
  460.       XOrY = _XOrY;
  461.       postcode = _postcode;
  462.     }
  463.   }
  464. }
  465.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement