用DotSpatial下载谷歌瓦片图并展示到地图控件上 【转】
http://blog.csdn.net/caoshiying/article/details/51991647
上一篇文章讲解如何加载各地图的WMS地图服务。虽然不涉及到瓦片,但是每次地图刷新都要请求网络,造成不小的网络负载。虽然判断视野是否改变确定是否请求网络来减小网络负载,但是这个方法仍然不理想。
谷歌的地图底图自带高程视觉,公路分级样式、行政区域分级样式、地图数据即时的更新速度等等优点,让人觉得有必要开发一个地图下载器。虽然谷歌本身被墙,但是谷歌地图还是可以访问的。地址如下:
http://www.google.cn/maps(可以手动输入:http://maps.google.cn)
一、新建WinForm项目
谷歌已经关闭了开发者API,现在只能自己动手做一个了。下面仍然新建一个WinForm程序,增加对DotSpatial的引用,加入DotSpatial控件,代码如下:
- using DotSpatial.Controls;
- using DotSpatial.Data;
- using DotSpatial.Projections;
- using DotSpatial.Topology;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- namespace GoogleWmts
- {
- public partial class MainForm : Form
- {
- private Map mapCtrl;
- private ProjectionInfo currentProjection;
- public const double WUHAN_WGS84_COORDINATE_Y = 30.883124;
- public const double WUHAN_WGS84_COORDINATE_X = 114.419915;
- private Coordinate wuhanCoordinate;
- public Size lastSize;
- public MainForm()
- {
- mapCtrl = new Map()
- {
- Left = 0,
- Top = 0,
- Size = new Size(0, 0),
- Dock = DockStyle.Fill,
- FunctionMode = FunctionMode.Pan
- };
- InitProjection();
- InitializeComponent();
- Controls.Add(mapCtrl);
- }
- private void InitProjection()
- {
- currentProjection = ProjectionInfo.FromEpsgCode(2432);
- var xy = new double[2] { WUHAN_WGS84_COORDINATE_X, WUHAN_WGS84_COORDINATE_Y };
- var z = new double[1];
- Reproject.ReprojectPoints(xy, z, KnownCoordinateSystems.Geographic.World.WGS1984, currentProjection, 0, 1);
- wuhanCoordinate = new Coordinate(xy);
- }
- }
- }
很遗憾的是DotSpatial内建的坐标系统并不支持Google的900913坐标系,这里我使用Beijing1954坐标系,中央经线是105度,EPSG的CRSID是2432,预定义武汉的经纬度,用于设定地图初始化视野。currentProjection定义当前地图控件使用的坐标系。天地图、OSM地图、腾讯地图、谷歌的地图都是基于分辨率设定视野,因此新建一个ResolutionLayer类型的图层,以便通用,代码如下:
- using DotSpatial.Controls;
- using DotSpatial.Data;
- using DotSpatial.Projections;
- using DotSpatial.Symbology;
- using DotSpatial.Topology;
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading.Tasks;
- namespace GoogleWmts
- {
- class ResolutionLayer : Layer, IMapLayer
- {
- public const double GOOGLE_ORIGIN_X = -20037508.3427892;
- public const double GOOGLE_ORIGIN_Y = 20037508.3427892;
- public const int TILE_WIDTH = 256;
- public const int TILE_HEIGHT = 256;
- public const int SCREEN_MILLIMETER_WIDTH = 564;
- public const int SCREEN_PIXEL_WIDTH = 1600;
- public const int MAX_ZOOM_LEVEL = 18;
- private Dictionary<int, double> resolutions;
- string urlFormat = "http://mt2.google.cn/vt/lyrs=m@167000000&hl=zh-CN&gl=cn&x={0}&y={1}&z={2}&s=Galil";//X瓦图号,Y瓦片号,比例尺缩放层级
- private Extent defaultExtent;
- public int ZoomLevel { get; set; }
- public Size WindowSize { get; set; }
- public bool WindowCreated { get; set; }
- public bool IsBusy { get; set; }
- public ResolutionLayer()
- {
- defaultExtent = new Extent(-2000000000, -2000000000, 2000000000, 2000000000);
- InitDirectory();
- SetResolutionsByMath();
- }
- private void InitDirectory()
- {
- var rpt = @"Tiles\Google\";
- if (!Directory.Exists(rpt))
- Directory.CreateDirectory(rpt);
- for (var i = 0; i <= MAX_ZOOM_LEVEL; i++)
- {
- var rp = rpt + @"\" + i;
- if (!Directory.Exists(rp))
- Directory.CreateDirectory(rp);
- }
- }
- public override Extent Extent
- {
- get
- {
- return defaultExtent;
- }
- }
- }
- }
GOOGLE_ORIGIN_X与GOOGLE_ORIGIN_Y两个常量记录谷歌地图的坐标原点,TILE_WIDTH与TILE_HEIGHT两个常量记录单个瓦片图文件的像素大小。SCREEN_MILLIMETER_WIDTH常量记录当前显示屏幕的物理大小。我当前的显示屏幕是1600像素的宽度,请朋友在使用之前务必改成你自己的屏幕的分辨率宽度。这里提供一个简单的方法获取屏幕物理大小与像素大小,代码如下:
- [DllImport("gdi32.dll", EntryPoint = "GetDeviceCaps", CallingConvention = CallingConvention.Winapi)]
- public static extern int GetDeviceCaps(IntPtr hdc, int code);
- public const int HORZSIZE = 4;
- var g = CreateGraphics();
- var millimeterLength = NativeAPI.GetDeviceCaps(g.GetHdc(), NativeAPI.HORZSIZE);
- var pixelLength = Screen.PrimaryScreen.Bounds.Width;
- g.Dispose();
MAX_ZOOM_LEVEL是最大缩放级别,也就是街道级别。resolutions是各层级比例尺下的分辨率。defaultExtent是给DotSpatial计算图层最大视野用的。此变量必须给,否则看不到地图。这与DotSpatial计算视野,确定窗口更新区域的算法有关系。放在这里吧。ZoomLevel
是当前缩放级别,WindowSize记录窗体的实际大小,WindowCreated指示窗口是否已经创建成功。IsBusy指示图层当前是否正在下载瓦片图。如果正在下载中,那么不响应用户放大、缩小、移动等地图操作。InitDirectory方法设定瓦片的存储路径,组织方式是在当前软件的文件夹下新建一个Tiles文件夹,再新建一个Google文件夹,然然针对每一个比例尺新建文件夹,瓦片图文件名称以瓦片索引命名。
谷歌地图的分辨率可能通过计算的方法获取,代码如下:
- private void SetResolutionsByMath()
- {
- resolutions = new Dictionary<int, double>();
- for (var i = 1; i <= MAX_ZOOM_LEVEL; i++)
- resolutions.Add(i, 20037508.3427892 * 2 / 256 / Math.Pow(2, i));
- }
二、坐标转换
上面说过,DotSpatial不支持Google的900913坐标系,那么必须进行坐标转换。在这里我使用Proj.4 C++库,并封装一个Win32动态库给C#调用,C++的调用Proj.4的代码如下:
- typedef __declspec(dllexport) struct _COORDINATE
- {
- double x;
- double y;
- double z;
- double m;
- int srid;
- }COORDINATE, *PCOORDINATE;
- BRIDGE_API BOOL proj4_transform(PCSTR proj4_from, PCSTR proj4_to, COORDINATE* coordinate)
- {
- if (proj4_from == nullptr || strlen(proj4_from) < 5)
- return FALSE;
- if (proj4_to == nullptr || strlen(proj4_to) < 5)
- return FALSE;
- projPJ from = pj_init_plus(proj4_from);
- projPJ to = pj_init_plus(proj4_to);
- if (from == nullptr || to == nullptr)
- return FALSE;
- int code = pj_transform(from, to, 1, 1, &coordinate->x, &coordinate->y, &coordinate->z);
- return !code;
- }
C#调用代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading.Tasks;
- namespace GK.Collector.Server.Entity.DllImports
- {
- [StructLayout(LayoutKind.Explicit)]
- public struct COORDINATE
- {
- [FieldOffset(0)]
- public double x;
- [FieldOffset(8)]
- public double y;
- [FieldOffset(16)]
- public double z;
- [FieldOffset(24)]
- public double m;
- [FieldOffset(32)]
- int srid;
- }
- }
- [DllImport("bridge.dll", EntryPoint = "proj4_transform", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern bool Proj4Transform(string proj4From, string projTo, IntPtr coordinate);
- public static bool Transform(double[] xyz, string proj4From, string proj4To = "")
- {
- if (xyz.Length < 3)
- return false;
- if (string.IsNullOrWhiteSpace(proj4From))
- return false;
- if (string.IsNullOrWhiteSpace(proj4To))
- proj4To = " +proj=longlat +datum=WGS84 +no_defs";
- var c = new COORDINATE() { x = xyz[0], y = xyz[1], z = xyz[2] };
- var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(c));
- Marshal.StructureToPtr(c, ptr, true);
- Proj4Transform(proj4From, proj4To, ptr);
- c = (COORDINATE)Marshal.PtrToStructure(ptr, typeof(COORDINATE));
- Marshal.FreeHGlobal(ptr);
- xyz[0] = c.x;
- xyz[1] = c.y;
- xyz[2] = c.z;
- return true;
- }
把常用的坐标系设定为字符串常量,以方便使用,代码如下:
- public const string BJ2432_PROJ = "+proj=tmerc +lat_0=0 +lon_0=105 +k=1 +x_0=500000 +y_0=0 +ellps=krass +towgs84=15.8,-154.4,-82.3,0,0,0,0 +units=m +no_defs ";
- public const string WORLD3857_PROJ = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs";
- public const string GOOGLE_PROJ = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs ";
- public const string WGS84_PROJ = "+proj=longlat +datum=WGS84 +no_defs ";
三、计算瓦片索引并下载
瓦片索引的计算是重中之重。其中包括瓦片对齐到用户窗口,避免地图移位、拖动不顺畅的问题,代码如下:
- /// <summary>
- /// 获取瓦片图索引以及偏移
- /// </summary>
- /// <param name="cxy">原始坐标</param>
- /// <param name="txy">瓦片图索引</param>
- /// <param name="rxy">偏移</param>
- private void GetTileIndexByCoordinate(double[] cxy, int[] txy, double[] rxy)
- {
- var z = new double[1];
- var coef = resolutions[ZoomLevel] * TILE_WIDTH;
- var wgs84Proj = KnownCoordinateSystems.Geographic.World.WGS1984;
- //DotSpatial.Projections.Reproject.ReprojectPoints(cxy, z, Projection, wgs84Proj, 0, 1);
- //cxy[0] = cxy[0] * 20037508.3427892 / 180;
- //cxy[1] = Math.Log(Math.Tan((90 + cxy[1]) * Math.PI / 360)) / (Math.PI / 180);
- //cxy[1] = cxy[1] * 20037508.3427892 / 180;
- Transform(cxy, BJ2432_PROJ, GOOGLE_PROJ);
- txy[0] = (int)((cxy[0] - GOOGLE_ORIGIN_X) / coef);
- txy[1] = (int)((GOOGLE_ORIGIN_Y - cxy[1]) / coef);
- rxy[0] = (cxy[0] - GOOGLE_ORIGIN_X) / coef - txy[0];
- rxy[1] = (GOOGLE_ORIGIN_Y - cxy[1]) / coef - txy[1];
- }
得到瓦片索引就可以下载了。用WebClient直接下载发现被谷歌屏蔽,通过Fiddler抓包工具发现可以顺利通过谷歌验证的HTTP包,代码如下:
- private Image GetImageByWebClient(double tilex, double tiley)
- {
- var rp = @"Tiles\Google\" + ZoomLevel + @"\" + tilex + "_" + tiley + ".png";
- if (File.Exists(rp))
- {
- var tb = Image.FromFile(rp);
- //Console.WriteLine(rp);
- return tb;
- }
- else
- {
- string url = string.Format(urlFormat, tilex, tiley, ZoomLevel);
- //Console.WriteLine(url);
- var downloader = new WebClient();
- downloader.Headers.Add("Upgrade-Insecure-Requests: 1");
- downloader.Headers.Add("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36");
- downloader.Headers.Add("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
- downloader.Headers.Add("Accept-Encoding: gzip, deflate, sdch");
- downloader.Headers.Add("Accept-Language: zh-CN,zh;q=0.8");
- try
- {
- var bts = downloader.DownloadData(url);
- var str = new MemoryStream(bts);
- var img = Image.FromStream(str);
- img.Save(rp);
- str.Close();
- str.Close();
- return img;
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- }
- downloader.Dispose();
- }
- return null;
- }
四、拼接瓦片图
拼接瓦片图的核心思想是找到左下角坐标对应的瓦片图索引与右上角坐标的瓦片图索引,通过一个循环就可以找到所有需要的瓦片图,并与文件系统已经存储的瓦片图缓存比对,缺少的就下载,已经有的就读取本地文件,代码如下:
- public void DrawRegions(MapArgs args, List<DotSpatial.Data.Extent> regions)
- {
- if (!WindowCreated || WindowSize.Width <= 160 || WindowSize.Height <= 30)
- return;
- IsBusy = true;
- var img = new Bitmap(args.ImageRectangle.Width, args.ImageRectangle.Height);
- var g = Graphics.FromImage(img);
- var resolution = resolutions[ZoomLevel];
- var toffset = new double[2];
- foreach (var region in regions)
- {
- var leftxy = new double[3] { region.MinX, region.MinY, 0 };
- var txy = new int[2];
- var rxy = new double[2];
- toffset[0] = -1;
- toffset[1] = -1;
- GetTileIndexByCoordinate(leftxy, txy, rxy);
- for (var i = region.MinX; i < region.MaxX; i += resolution * TILE_WIDTH)
- {
- for (var j = region.MinY; j < region.MaxY ; j += resolution * TILE_HEIGHT)
- {
- var tb = GetImageByWebClient(txy[0] + toffset[0], txy[1] + toffset[1]);
- var tx = Convert.ToInt32((toffset[0] - rxy[0]) * TILE_WIDTH);
- var ty = Convert.ToInt32((toffset[1] - rxy[1]) * TILE_HEIGHT);
- g.DrawImage(tb, tx, ty);
- toffset[1]++;
- }
- toffset[0]++;
- toffset[1] = 0;
- }
- }
- args.Device.DrawImage(img, 0, 0);
- g.Dispose();
- img.Dispose();
- IsBusy = false;
- }
下面两个方法用来计算有效的视野,给地图初始化之用,代码如下:
- public Extent GetAvailableExtent(Coordinate center, Size rc)
- {
- var ext = new Extent();
- var horizontal = resolutions[ZoomLevel] * rc.Width;
- var vertical = resolutions[ZoomLevel] * rc.Height;
- ext.MinX = center.X - horizontal / 2;
- ext.MinY = center.Y - vertical / 2;
- ext.MaxX = center.X + horizontal / 2;
- ext.MaxY = center.Y + vertical / 2;
- return ext;
- }
- public void GetDistance(double[] xy)
- {
- var resolution = resolutions[ZoomLevel];
- xy[0] = xy[0] * resolution;
- xy[1] = xy[1] * resolution;
- }
至此瓦片图图层完成。
五、瓦片图地图函数
DotSpatial地图控件默认没有比例尺,也就是自由比例尺,可以无限制的缩放。而在线地图只有18个缩放级别,如果不用地图函数限制DotSpatial地图控件的行为,就会导致地图移位。代码如下:
- using DotSpatial.Controls;
- using DotSpatial.Topology;
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace GoogleWmts
- {
- class TileMapFunction : MapFunction
- {
- private int zoomLevel = 11;
- private System.Drawing.Point firstPoint;
- private System.Drawing.Point lastPoint;
- private ResolutionLayer layer;
- public TileMapFunction(IMap mapCtrl, ResolutionLayer layer) :
- base(mapCtrl)
- {
- this.layer = layer;
- }
- protected override void OnMouseDown(GeoMouseArgs e)
- {
- firstPoint = e.Location;
- base.OnMouseDown(e);
- }
- protected override void OnMouseUp(GeoMouseArgs e)
- {
- lastPoint = e.Location;
- //var offset = new double[2] { firstPoint.X - lastPoint.X, firstPoint.Y - lastPoint.Y };
- //layer.GetDistance(offset);
- //Map.ViewExtents.SetCenter(new Coordinate(Map.ViewExtents.Center.X + offset[0], Map.ViewExtents.Center.Y + offset[1]));
- base.OnMouseUp(e);
- }
- protected override void OnMouseWheel(GeoMouseArgs e)
- {
- e.Handled = true;
- if (layer.IsBusy)
- return;
- if (e.Delta > 0)
- zoomLevel++;
- else
- zoomLevel--;
- if (zoomLevel < 0)
- zoomLevel = 0;
- if (zoomLevel > ResolutionLayer.MAX_ZOOM_LEVEL)
- zoomLevel = ResolutionLayer.MAX_ZOOM_LEVEL;
- layer.ZoomLevel = zoomLevel;
- Console.WriteLine("中心点:" + Map.ViewExtents.Center.X + "," + Map.ViewExtents.Center.Y);
- //Map.ViewExtents = layer.GetAvailableExtent(Map.ViewExtents.Center, Map.ClientRectangle.Size);
- base.OnMouseWheel(e);
- }
- protected override void OnMouseMove(GeoMouseArgs e)
- {
- e.Handled = true;
- base.OnMouseMove(e);
- }
- }
- }
六、完善WinForm窗口事件
首先声明瓦片图图层与地图函数对象,加入到地图控件,代码如下:
- private ResolutionLayer layer;
- private TileMapFunction func;
- layer = new ResolutionLayer()
- {
- Projection = currentProjection,
- WindowSize = mapCtrl.Size,
- ZoomLevel = 10
- };
- func = new TileMapFunction(mapCtrl, layer);
- mapCtrl.Layers.Add(layer);
- mapCtrl.Projection = currentProjection;
- mapCtrl.MapFunctions.Add(func);
- mapCtrl.ActivateMapFunction(func);
在窗口完成加载时初始化地图视野,设置图层记录的窗口大小以通知图层准备绘图。
- protected override void OnLoad(EventArgs e)
- {
- base.OnLoad(e);
- layer.WindowCreated = true;
- mapCtrl.ViewExtents = layer.GetAvailableExtent(wuhanCoordinate, layer.WindowSize);
- }
处理窗品大小改变事件,使地图始终铺满窗口,代码如下:
- protected override void OnSizeChanged(EventArgs e)
- {
- base.OnSizeChanged(e);
- if(lastSize!=Size)
- {
- lastSize = Size;
- if (WindowState != FormWindowState.Minimized && mapCtrl.Width <= 0)
- {
- layer.WindowSize = this.Size;
- mapCtrl.Size = this.Size;
- Console.WriteLine("中心点:" + mapCtrl.ViewExtents.Center.X + "," + mapCtrl.ViewExtents.Center.Y);
- }
- }
- }
七、最终效果
用DotSpatial下载谷歌瓦片图并展示到地图控件上 【转】的更多相关文章
- my97DatePicker日期控件——日期输入框联动,使用focus使第二个输入框没展示出日期控件
描述问题场景: 1.jquery使用的版本是jquery-1.7.2.min.js 2.代码不是写在页面上的,是通过事件后追加的 <!DOCTYPE html> <html> ...
- MFC的PictureControl控件上展示Halcon的窗体
自定义函数:id是MFC窗体上的PictureControl控件的ID,此函数返回halcon的HWindow对象. HTuple initPicControl(int id); HTuple CHa ...
- 基于MySQL + Node.js + Leaflet的离线地图展示,支持百度、谷歌、高德、腾讯地图
1. 基本说明 本项目实现了离线展示百度.谷歌.高德.腾讯地图.主要功能如下: 实现了地图瓦片图下载.存储.目前支持存储至MySQL Node.js服务调用MySQL中的瓦片图 Leaflet展示地图 ...
- High-speed Charting Control--MFC绘制图表(折线图、饼图、柱形图)控件
原文地址:https://www.codeproject.com/articles/14075/high-speed-charting-control 本文翻译在CodeProject上的介绍(主要还 ...
- [转 载] android 谷歌 新控件(约束控件 )ConstraintLayout 扁平化布局
序 在Google IO大会中不仅仅带来了Android Studio 2.2预览版,同时带给我们一个依赖约束的库. 简单来说,她是相对布局的升级版本,但是区别与相对布局更加强调约束.何为约束,即控件 ...
- 将展示内容(div、iframe)放在Expand控件中
Expand是ArcGIS JavaScript API 4.3推出的一个widget(控件),用于承载一个HTML DOM元素,可以把一个自己编写的div或者是一个其他的Esri widget控件放 ...
- openlayers4 入门开发系列之地图导航控件篇(附源码下载)
前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...
- C# 导出一个控件的矢量图
调用Control.DrawToBitmap(Bitmap) 方法是很容易得到控件的图形的. 但是bitmap是栅格化图形.栅格化图形有很多缺点,比如文件体积比较大. 放大后失真. 不易编辑等等. 这 ...
- 过实现鹰眼图这个功能来进一步学习MapControl控件
我们通过实现鹰眼图这个功能来进一步学习MapControl控件.在实现鹰眼图之前,我们需 要接口有更深入的了解. 变主动为被动-出接口(OutBound interface) COM编程类似客户端和服 ...
随机推荐
- 简单的FreeBSD 的内核编译
简单的FreeBSD 的内核编译 删除并重新下载内核源码 删除自带的内核源码rm -rf /usr/src 下载内核源码wget https://download.freebsd.org/ftp/re ...
- axis2实践(二)Restful入门示例
1. 实例说明 本示例直接参照了RESTful Web Services with Apache Axis2,本示例基本就是沿用的原示例,就是一个对学生信息(包括姓名,年龄,课程)的管理的例子,提供如 ...
- [CF522D]Closest Equals
题目大意:给一个区间,多次询问,每次问区间$[l,r]$里最近的两个相同的数的距离是多少. 题解:用一个数组$pre_i$表示第$i$个数前面最近的一个相同的数在哪,询问变成了询问$[l,r]$中$i ...
- Educational Codeforces Round 42 (Rated for Div. 2) B
B. Students in Railway Carriage time limit per test 2 seconds memory limit per test 256 megabytes in ...
- bzoj1009 [HNOI2008] GT考试 矩阵乘法+dp+kmp
1009: [HNOI2008]GT考试 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 4542 Solved: 2815[Submit][Statu ...
- bzoj 2159 - Crash 的 文明世界
Description 给定一棵\(n\le 10^5\)的树, 和\(k\le 150\) 求每个点\(x\)的\[S(x) = \sum_{y=1}^n dis(x, y) ^ k\] Analy ...
- usb驱动---linux ACM驱动详解ACA【转】
转自:http://blog.chinaunix.net/uid-9185047-id-3404684.html DTE提供或接收数据,连接到网络中的用户端机器,主要是计算机和终端设备.与此相对地,在 ...
- 【一】ODB - C++ 访问数据库的利器--Hello World On Windows(Version-24)
本文以MySQL数据库为例,其他数据类似. 官方文档和下载 ODB官方首页 官方开发者说明书(开发教程) 安装下载首页(下载与安装教程Windows/Linux) Windows安装步骤(都是英 ...
- Windows8 上用Ubuntu-Ubuntu启动SSH
公司刚给配了一个电脑,华硕的超级本8个G的内存,很强大的了,但是系统是64位的windows 8,我用wubi.exe直接安装到系统上,但是开机启动的时候总是下面这个错误,去Ubuntu社区请教,结论 ...
- Appium+python自动化19-iOS模拟器(iOS Simulator)安装自家APP【转载】
前言 做过iOS上app测试的小伙伴应该都知道,普通用户安装app都是从appstore下载安装,安装测试版本的app,一般就是开发给的二维码扫码安装, 或者开发给个.ipa的安装包文件,通过itoo ...