打造地圖拼接利器(五)地圖采集與拼接

獲取到經(jīng)緯度范圍后,我們需要計(jì)算出瓦片的范圍。

本文涉及的地圖瓦片都以左上角為原點(diǎn)開始編號的娃肿,從左至右為 x 軸, 從上到下為 y軸珠十。

坐標(biāo)示意圖

為保證地圖是方形料扰,基于 Web 墨卡托投影的地圖左上角經(jīng)緯度坐標(biāo)為(180°,85.0511 °)焙蹭,右下角經(jīng)緯度為(-180°晒杈,-85.0511°)。緯度范圍是[-85.0511, 85.0511 ]孔厉。

假設(shè)z為需要拼接的圖層的層數(shù)拯钻,設(shè)n=2的z次方,lon為經(jīng)度撰豺,lat為維度粪般,則經(jīng)緯度、層級和瓦片的坐標(biāo)x郑趁、y的關(guān)系為:

TileX =(lon+180)÷360×n刊驴;
TileY = (1-(log(tan(lat))+sec(lat)))÷2×n;

在C#中寡润,Math函數(shù)的三角參數(shù)要求為弧度,所以緯度值牽扯到三角函數(shù)的還需要×PI÷180舅柜。兩個(gè)函數(shù)如下:

     /// <summary>
        /// 計(jì)算瓦片所在X值
        /// </summary>
        /// <param name="lng"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
        public static double getTileX(double lng, int zoom)
        {
            double tileX = (lng + 180.0) / 360 * Math.Pow(2, zoom);
            return tileX;
        }

        /// <summary>
        /// 墨卡托投影下梭纹,通過維度計(jì)算瓦片所在Y值
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
        public static double getTileY(double lat, int zoom)
        {
            if (lat > 90)
                lat = lat - 180;
            if (lat < -90)
                lat = lat + 180;
            double tileY = Math.Pow(2, zoom) / 2 * (1 - (Math.Log(Math.Tan(Math.PI * lat / 180) + 1 / Math.Cos(Math.PI * lat / 180))) / Math.PI);
            return tileY;
        }

前面將下載圖片的函數(shù)已在第三章中寫好,直接通過xyz和瓦片地址下載圖片致份,然后使用GDI+拼接即可变抽。

這里有一個(gè)很嚴(yán)重的問題,就是受GDI+技術(shù)限制,圖幅過大可能拼接失敗绍载。一般10000像素×10000像素以內(nèi)為妥诡宗。所以,過大范圍的需求击儡,只能拼接為系統(tǒng)承受范圍內(nèi)的大小了塔沃。我們做一個(gè)參數(shù),可以設(shè)置這個(gè)值阳谍,最大到10240像素蛀柴。用戶最終在ps里再量力而行的拼接了。

寫一個(gè)類DownloadWork矫夯,用于在線程里展開下載任務(wù)鸽疾,在構(gòu)造函數(shù)中聲明下載范圍和下載圖片的參數(shù)。

GetArea(WorkArg arg)用于對任務(wù)進(jìn)行拆解训貌,以防止GDI+內(nèi)存溢出報(bào)錯(cuò):

  /// <summary>
        /// 以maxnum×maxnum為一組數(shù)據(jù)
        /// </summary>
        /// <param name="Arg"></param>
        /// <returns></returns>
        private List<AreaConfig> GetAreas(WorkArg Arg)
        {
            List<AreaConfig> results = new List<AreaConfig>();

            for (int j = Arg.Starty, n = 0; j <= Arg.Endy; n++)
            {
                for (int i = Arg.Startx, m = 0; i <= Arg.Endx; m++)
                {
                    AreaConfig area = new AreaConfig();
                    area.Startx = i;
                    area.Endx = i + GlobalConfig.maxtilecount < Arg.Endx ? i + GlobalConfig.maxtilecount : Arg.Endx + 1;
                    area.Starty = j;
                    area.Endy = j + GlobalConfig.maxtilecount < Arg.Endy ? j + GlobalConfig.maxtilecount : Arg.Endy + 1;
                    area.Posx = m;
                    area.Posy = n;
                    i += GlobalConfig.maxtilecount;
                    results.Add(area);
                }
                j += GlobalConfig.maxtilecount;
            }
            return results;
        }

主下載拼接函數(shù)主要有以下任務(wù):一是拆分任務(wù)制肮,二是根據(jù)任務(wù)大小,創(chuàng)建一個(gè)bitmap递沪,三是循環(huán)x和y下載圖豺鼻,并根據(jù)坐標(biāo)繪制在bitmap上,四是保存bitmap到本地磁盤区拳。其中拘领,在三中還完成了復(fù)合圖的制作,代碼如下樱调。

  public void DoWork(object arg)
        {
            WorkArg Arg = (WorkArg)arg;
            FileInfo fInfo=new FileInfo(Arg.Filename);
            string savedir = "";

            int xNum = (Arg.Endx - Arg.Startx + 1), yNum = (Arg.Endy - Arg.Starty + 1);
            int totalnum=xNum*yNum,count = 0;
            //受GDI的限制约素,每次拼圖不能超過20000像素×20000像素,但實(shí)際操作過程中笆凌,遠(yuǎn)不止這個(gè)數(shù)圣猎,有時(shí)候10000像素也會(huì)出錯(cuò),為了程序的穩(wěn)定性乞而,
            //如果設(shè)置單幅圖最大不超過20×20個(gè)瓦片送悔,就是5120×5120像素
            //此處需要對數(shù)據(jù)進(jìn)行拆分
            List<AreaConfig> tasks = GetAreas(Arg);

            for (int taski = 0; taski < tasks.Count; taski++)
            {
                int xnum = tasks[taski].Endx - tasks[taski].Startx, ynum = tasks[taski].Endy - tasks[taski].Starty;
                Bitmap bit = new Bitmap(xnum * 256, ynum * 256);
                Graphics g = Graphics.FromImage(bit);

                for (int i = tasks[taski].Startx, m = 0; i < tasks[taski].Endx; i++, m++)
                {
                    for (int j = tasks[taski].Starty, n = 0; j < tasks[taski].Endy; j++, n++)
                    {
                        if (!iswork)
                            break;
                        foreach (var url in Arg.Downloadurls)
                        {
                            string durl = Tools.getUrl(url, i, j, Arg.Zoom);
                            byte[] bts = Tools.downloadTile(durl);
                            if (bts == null)
                            {
                                g.DrawString("此區(qū)域請求失敗", new Font("宋體", 12f), new SolidBrush(Color.Red), new Point(m * 256, n * 256));
                            }
                            else
                            {
                                MemoryStream ms = new MemoryStream(bts);
                                Image img = Bitmap.FromStream(ms);
                                g.DrawImage(img, m * 256, n * 256, 256, 256);
                            }
                        }
                        count++;
                        myWorkPercent(count, totalnum,taski+1,tasks.Count);
                    }
                    if (!iswork)
                        break;
                }
                string filename = fInfo.DirectoryName + "\\" + fInfo.Name + "\\";
                if (!Directory.Exists(filename))
                    Directory.CreateDirectory(filename);
                savedir = filename;
                if (tasks.Count > 1)
                {
                    filename += tasks[taski].Posy + "-" + tasks[taski].Posx + "_";
                }
                filename+=fInfo.Name;
                bit.Save(filename, ImageFormat.Jpeg);
                g.Dispose();
                bit.Dispose();
            }
            myFinishWork(savedir);
        }

最后,啟動(dòng)線程:

 public void StartWork()
        {
            iswork = true;
            th = new Thread(new ParameterizedThreadStart(DoWork));
            th.Start(this.workarg);
        }

增加2個(gè)事件代理爪模,分別傳回工作狀態(tài)和合并完成狀態(tài):

    public delegate void Workpercent(int curnum, int maxnum, int ctasknum, int tasknum);
    public delegate void FinishWork(string filedir);

合并完成后欠啤,通知主界面打開任務(wù)文件夾:

      void dlw_myFinishWork(string savedir)
        {
            MessageBox.Show("任務(wù)完成!");
            enableControls(true);
            System.Diagnostics.Process.Start("explorer.exe", savedir);
            dlw = null;
        }

至此屋灌,所有工作基本完成洁段。最后在啰嗦2點(diǎn):

1.最好使用天地圖,占位高共郭,不足就是每天數(shù)量有限制祠丝,可以自己申請開發(fā)者的tk疾呻,改config.txt后下載。

2.國外圖只作為參考写半,邊境問題敏感岸蜗,立場要堅(jiān)定,使用要謹(jǐn)慎叠蝇。

鏈接:https://pan.baidu.com/s/1dGzf3GRY50cqmBL-oA-PGA
提取碼:v77a
復(fù)制這段內(nèi)容后打開百度網(wǎng)盤手機(jī)App璃岳,操作更方便哦

本章參考:

芒果香蕉_《地圖瓦片編號與經(jīng)緯度的換算關(guān)系》簡書

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蟆肆,隨后出現(xiàn)的幾起案子矾睦,更是在濱河造成了極大的恐慌,老刑警劉巖炎功,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枚冗,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛇损,警方通過查閱死者的電腦和手機(jī)赁温,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淤齐,“玉大人股囊,你說我怎么就攤上這事「模” “怎么了稚疹?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長祭务。 經(jīng)常有香客問我内狗,道長,這世上最難降的妖魔是什么义锥? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任柳沙,我火速辦了婚禮,結(jié)果婚禮上拌倍,老公的妹妹穿的比我還像新娘赂鲤。我一直安慰自己,他們只是感情好柱恤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布数初。 她就那樣靜靜地躺著,像睡著了一般梗顺。 火紅的嫁衣襯著肌膚如雪妙真。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天荚守,我揣著相機(jī)與錄音珍德,去河邊找鬼。 笑死矗漾,一個(gè)胖子當(dāng)著我的面吹牛锈候,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播敞贡,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼泵琳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了誊役?” 一聲冷哼從身側(cè)響起获列,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛔垢,沒想到半個(gè)月后击孩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鹏漆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年巩梢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艺玲。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡括蝠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饭聚,到底是詐尸還是另有隱情忌警,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布秒梳,位于F島的核電站法绵,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏端幼。R本人自食惡果不足惜礼烈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望婆跑。 院中可真熱鬧此熬,春花似錦、人聲如沸滑进。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扶关。三九已至阴汇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間节槐,已是汗流浹背搀庶。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工拐纱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哥倔。 一個(gè)月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓秸架,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咆蒿。 傳聞我的和親對象是個(gè)殘疾皇子东抹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容