学习WorldWind有很长时间了,理论学习算是基本完成了。我体会是WW的学习主要分为两大步:WW框架体系学习和WW插件学习。学习WW插件逐步深入后,必然要首先学习Direct3D编程,这也算是我的经验之谈吧。今天Virtual Earth插件学习完成,也标志着我可以从WW理论转向WW实践啦。虽然我总结介绍的是Virtual Earth插件,但是希望网友阅读下面的内容前,最好能够先深入学习Direct3D编程、BMNG和Globe Icon插件的底层渲染,这些都是学习Virtual Earth的基础。

 Virtual Earth插件包括以下几个类:

  VirtualEarthForm 窗体类

  VirtualEarthPlugin插件类,继承自Plugin(重要)

  VeReprojectTilesLayer 渲染对象类,继承自Renderable Object(重要)

  VeTile 瓦片对象类(真正实现大部分功能的)(重要)

  Projection投影变换类(重要)

  Search类

  PushPin类

  我们先看看VirtualEarthPlugin,所有的插件类必须继承自Plugin.cs,必须重写Load()和Unload()方法。这两个方法分别实现插件的加载和卸载。

  Load()方法一般是实现添加菜单和添加工具按钮,跟前面介绍插件都很类似的,自己开发插件时模仿着这套路写就行。

  public override void Load()
        {
            try
            {
          //判断当前World为Earth(注:其他星体可通过名字判断)
                if (ParentApplication.WorldWindow.CurrentWorld.IsEarth)
                {  //初始化VE插件控制窗体
                    m_Form = new VirtualEarthForm(ParentApplication);
                    m_Form.Owner = ParentApplication;
           //添加VE插件菜单
                    m_MenuItem = new MenuItem("MicroSoft VirtualEarth");
                    m_MenuItem.Click += new EventHandler(menuItemClicked);
                    ParentApplication.PluginsMenu.MenuItems.Add(m_MenuItem);

//#if DEBUG
                    string imgPath = Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + "\\Plugins\\VirtualEarth\\VirtualEarthPlugin.png";
                    //#else
                    //                    _pluginDir = this.PluginDirectory;
                    //                    string imgPath = this.PluginDirectory + @"\VirtualEarthPlugin.png";
                    //#endif
                    if (File.Exists(imgPath) == false)
                    {
                        Utility.Log.Write(new Exception("imgPath not found " + imgPath));
                    }

            //添加工具按钮,会出现工具栏里
                    m_ToolbarItem = new WorldWind.WindowsControlMenuButton(
                        "MicroSoft VirtualEarth",
                        imgPath,
                        m_Form);

ParentApplication.WorldWindow.MenuBar.AddToolsMenuButton(m_ToolbarItem);

base.Load();
                }
            }
            catch (Exception ex)
            {
                Utility.Log.Write(ex);
                throw;
            }
        }

  Unload()方法就是释放插件窗体,并移除插件菜单项和插件工具按钮。

  我们再来看看VeReprojectTilesLayer 类,像其他插件类一样,要重载Initialize()、Update()、Render()方法,但是,VE插件重点在Upadate()方法,当然他还有其他自己特色的方法。

  Initialize()方法主要是一些该类全局对象的实例化,如:投影、VE控制窗体及VeTile初始化等。

  Render()方法主要是实现插件的三维渲染功能的,但是VE的Render真正实现是调用VeTile类中的Render()方法。

VE插件渲染代码

 /// <summary>
        /// Draws the layer
        /// </summary>
        public override void Render(DrawArgs drawArgs)
        {
            try
            {
                if (this.isOn == false)
                {
                    return;
                }

if (this.isInitialized == false)
                {
                    return;
                }

if (drawArgs.device == null)
                    return;

if (veTiles != null && veTiles.Count > 0)
                {
                    //render mesh and tile(s)
                    bool disableZBuffer = false; //TODO where do i get this setting
                    //foreach(VeTile veTile in veTiles)
                    //{
                    //    veTile.Render(drawArgs, disableZBuffer);
                    //}

// camera jitter fix
                    drawArgs.device.Transform.World = Matrix.Translation(
                           (float)-drawArgs.WorldCamera.ReferenceCenter.X,
                        (float)-drawArgs.WorldCamera.ReferenceCenter.Y,
                        (float)-drawArgs.WorldCamera.ReferenceCenter.Z
                    );

// Clear ZBuffer between layers (as in WW)
                    drawArgs.device.Clear(ClearFlags.ZBuffer, 0, 1.0f, 0);

// Render tiles(这里的GetZoomLevelByTrueViewRange方法是个知识点,该方法是根据WorldCamera视角范围,求取当前球体的缩放层次)
            int zoomLevel = GetZoomLevelByTrueViewRange(drawArgs.WorldCamera.TrueViewRange.Degrees);

        
         //真正的渲染处理是由VeTile类的Render()方法实现的
          int tileDrawn = VeTile.Render(drawArgs, disableZBuffer, veTiles, zoomLevel); // Try current level first
         if(tileDrawn == 0) VeTile.Render(drawArgs, disableZBuffer, veTiles, prevLvl); // If nothing drawn, render previous level

//camera jitter fix
                    drawArgs.device.Transform.World = drawArgs.WorldCamera.WorldMatrix;

//Render logo
                    RenderDownloadProgress(drawArgs, null, 0);

}

//else pushpins only
                //render PushPins
                if (pushPins != null && pushPins.Count > 0)
                {

RenderPushPins(drawArgs);

}
            }
            catch (Exception ex)
            {
                Utility.Log.Write(ex);
            }
        }

  VE中获取缩放级别的方法是很巧妙的,根据原作者文章知:VE的缩放级别是1-19级甚至更低,是以2的阶乘递减的。方法我们自己开发时可以重用,但是它的思想还是要好好体会的。

  注:缩放级别为 180 、90、45、22.5、……

根据视角范围获取缩放级别

        public int GetZoomLevelByTrueViewRange(double trueViewRange)
        {
            int maxLevel = 3; //视角范围为45度
            int minLevel = 19;
            int numLevels = minLevel - maxLevel + 1;
            int retLevel = maxLevel;
            for (int i = 0; i < numLevels; i++)
            {
                retLevel = i + maxLevel;

double viewAngle = 180;
                for (int j = 0; j < i; j++)
                {
                    viewAngle = viewAngle / 2.0;
                }
                if (trueViewRange >= viewAngle)
                {
                    break;
                }
            }
            return retLevel;
        }

  VE插件最最关键的方法为Update(),因为VE实质上就是不断地根据缩放级别更新影像数据,其实就是构建要渲染绘制的对象集合,最后由Render()方法完成渲染绘制。所以该方法是VE中最复杂的,当然也就是VE的处理精华所在,也就是我们研究学习的重点啦。(说这些,主要是告诉大家这里是重点,要好好研究和关注)

Update()代码

        /// <summary>
        /// Update layer (called from worker thread)
        /// </summary>
        public override void Update(DrawArgs drawArgs)
        {

try
            {
                if (this.isOn == false)
                {
                    return;
                }

//NOTE for some reason Initialize is not getting called from the Plugin Menu Load/Unload
                //it does get called when the plugin loads from Startup
                //not sure what is going on, so i'll just call it manually
                if (this.isInitialized == false)
                {
                    this.Initialize(drawArgs);
                    return;
                }

//get lat, lon 获取经纬度、倾斜角度、高度等
                double lat = drawArgs.WorldCamera.Latitude.Degrees;
                double lon = drawArgs.WorldCamera.Longitude.Degrees;
                double tilt = drawArgs.WorldCamera.Tilt.Degrees;
                //determine zoom level
                double alt = drawArgs.WorldCamera.Altitude;
                //could go off distance, but this changes when view angle changes
                //Angle fov = drawArgs.WorldCamera.Fov; //stays at 45 degress
                //Angle viewRange = drawArgs.WorldCamera.ViewRange; //off of distance, same as TVR but changes when view angle changes

               //获取当前视角范围      
                Angle tvr = drawArgs.WorldCamera.TrueViewRange; //off of altitude
                //smallest altitude = 100m
                //tvr = .00179663198575926
                //start altitude = 12756273m
                //tvr = 180

//WW _levelZeroTileSizeDegrees 获取缩放级别,上面已经介绍啦
                //180 90 45 22.5 11.25 5.625 2.8125 1.40625 .703125 .3515625 .17578125 .087890625 0.0439453125 0.02197265625 0.010986328125 0.0054931640625
                int zoomLevel = GetZoomLevelByTrueViewRange(tvr.Degrees);

 
         //只要到一定缩放级别时,才启用VE(这里我们可以学习,来控制一些图层的显示)
                //dont start VE tiles until a certain zoom level
                if (zoomLevel < veForm.StartZoomLevel)
                {
                    this.RemoveAllTiles();
            this.ForceRefresh();
                    return;
                }

//WW tiles
                //double tileDegrees = GetLevelDegrees(zoomLevel);
                //int row = MathEngine.GetRowFromLatitude(lat, tileDegrees);
                //int col = MathEngine.GetColFromLongitude(lon, tileDegrees);

//VE tiles
                double metersY;
                double yMeters;
                int yMetersPerPixel;
                int row;
                /*
                //WRONG - doesn't stay centered away from equator
                //int yMeters = LatitudeToYAtZoom(lat, zoomLevel); //1024
                double sinLat = Math.Sin(DegToRad(lat));
                metersY = earthRadius / 2 * Math.Log((1 + sinLat) / (1 - sinLat)); //0
                yMeters = earthHalfCirc - metersY; //20037508.342789244
                yMetersPerPixel = (int) Math.Round(yMeters / MetersPerPixel(zoomLevel));
                row = yMetersPerPixel / pixelsPerTile;
                */
                //CORRECT
                //int xMeters = LongitudeToXAtZoom(lon, zoomLevel); //1024

        //计算弧长,(earthRadius 地球半径,DegToRad(lon)经度的度值转弧度值)
        //DegToRad()为角度转弧度的方法,很简单。弧长=半径*弧度值。(这里球体的弧度值认为有负值)
                double metersX = earthRadius * DegToRad(lon); //0
              //因为从西到东算列数,所以要从-180度算列所在的弧度的总长度的      
                double xMeters = earthHalfCirc + metersX; //20037508.342789244
         //获取总的像素数。MetersPerPixel(zoomLevel))是计算每像素代表的米数。(知识点)
                int xMetersPerPixel = (int)Math.Round(xMeters / MetersPerPixel(zoomLevel));
         //获取列数,从西-》东(-180-》180)来算列数
                int col = xMetersPerPixel / pixelsPerTile;
          
                //reproject - overrides row above
                //this correctly keeps me on the current tile that is being viewed
        //使用UV结构体来存放经纬度,然后调用proj.Forward(uvCurrent)实现投影变换
                UV uvCurrent = new UV(DegToRad(lon), DegToRad(lat));
                uvCurrent = proj.Forward(uvCurrent);
                metersY = uvCurrent.V;
         //这里为啥是“—”??看过该插件原作者的文章才会知道:原来VE的行数是从北向南算的,而WW是从南向北计算行数的。
                yMeters = earthHalfCirc - metersY;
         //获取总像素数
                yMetersPerPixel = (int)Math.Round(yMeters / MetersPerPixel(zoomLevel));
         //获取行数
                row = yMetersPerPixel / pixelsPerTile;
说明:计算行列数,是为了后面获取切片后图片,并将正确的图片作为纹理渲染到正确的位置上,当然这过程还有很多处理,我会一一分析的。
                //update mesh if VertEx changes
                if (prevVe != World.Settings.VerticalExaggeration)
                {
                    lock (veTiles.SyncRoot)
                    {
                        VeTile veTile;
                        for (int i = 0; i < veTiles.Count; i++)
                        {
                            veTile = (VeTile)veTiles[i];
                            if (veTile.VertEx != World.Settings.VerticalExaggeration)
                            {
              //创建网格MESH(稍后重点分析)
                         veTile.CreateMesh(this.Opacity, World.Settings.VerticalExaggeration);
                            }
                        }
                    }
                }
                prevVe = World.Settings.VerticalExaggeration;

//if within previous bounds and same zoom level, then exit
                if (row == prevRow && col == prevCol && zoomLevel == prevLvl && tilt == preTilt)
                {
                    return;
                }

//System.Diagnostics.Debug.WriteLine("CHANGE");

lock (veTiles.SyncRoot)
                {
                    VeTile veTile;

           //使之前存放的veTile,标记为“暂时不需要”,这里没有删除
                    for (int i = 0; i < veTiles.Count; i++)
                    {
                        veTile = (VeTile)veTiles[i];
                        veTile.IsNeeded = false;
                    }
                }

//metadata
                ArrayList alMetadata = null;
                if (veForm.IsDebug == true)
                {
                    alMetadata = new ArrayList();
                    alMetadata.Add("yMeters " + yMeters.ToString());
                    alMetadata.Add("metersY " + metersY.ToString());
                    alMetadata.Add("yMeters2 " + yMeters.ToString());
                    alMetadata.Add("vLat " + uvCurrent.V.ToString());
                    //alMetadata.Add("xMeters " + xMeters.ToString());
                    //alMetadata.Add("metersX " + metersX.ToString());
                    //alMetadata.Add("uLon " + uvCurrent.U.ToString());
                }
         //添加目前VeTile(这是重点,稍后分析)
                //add current tiles first
                AddVeTile(drawArgs, row, col, zoomLevel, alMetadata);
                //then add other tiles outwards in surrounding circles

          //添加周边的VeTile
                AddNeighborTiles(drawArgs, row, col, zoomLevel, null, 1);
                AddNeighborTiles(drawArgs, row, col, zoomLevel, null, 2);
                AddNeighborTiles(drawArgs, row, col, zoomLevel, null, 3);
        // Extend tile grid if camera tilt above some values
                //根据倾斜角度决定是否继续添加相邻Tile单位          
                if(tilt > 45) AddNeighborTiles(drawArgs, row, col, zoomLevel, null, 4);
                if(tilt > 60) AddNeighborTiles(drawArgs, row, col, zoomLevel, null, 5);

//if(prevLvl > zoomLevel) //zooming out
                //{
                //}

lock (veTiles.SyncRoot)
                {
                    VeTile veTile;

            //移除不需要的veTile图片,为啥是现在移除??(思考一下有啥好处)
                    for (int i = 0; i < veTiles.Count; i++)
                    {
                        veTile = (VeTile)veTiles[i];
                        if (veTile.IsNeeded == false)
                        {
                            veTile.Dispose();
                            veTiles.RemoveAt(i);
                i--;
                        }
                    }
                }
        //保存当前基本的行列、缩放级别、倾斜角度
                prevRow = row;
                prevCol = col;
                prevLvl = zoomLevel;
        preTilt = tilt;
            }
            catch (Exception ex)
            {
                Utility.Log.Write(ex);
            }

}

我们来看看AddVeTile(drawArgs, row, col, zoomLevel, alMetadata);方法,因为该方法是AddNeighborTiles(drawArgs, row, col, zoomLevel, null, 1);方法的基础。

AddVeTile()方法代码

private void AddVeTile(DrawArgs drawArgs, int row, int col, int zoomLevel, ArrayList alMetadata)
        {
            //TODO handle column wrap-around
            //haven't had to explicitly handle this yet

bool tileFound = false;
            lock (veTiles.SyncRoot)
            {
                foreach (VeTile veTile in veTiles)
                {
                    if (veTile.IsNeeded == true)
                    {
                        continue;
                    }

  //判断当前的veTile对象,是否是当前缩放级别下的行列对应的“瓦片”,这里涉及到VeTile类的IsEqual()方法
                    if (veTile.IsEqual(row, col, zoomLevel) == true)
                    {
                        veTile.IsNeeded = true;
                        tileFound = true;
                        break;
                    }
                }
            }
            if (tileFound == false)
            {
                //exit if zoom level has changed

               //获取当前新的缩放级别
                int curZoomLevel = GetZoomLevelByTrueViewRange(drawArgs.WorldCamera.TrueViewRange.Degrees);
                if (curZoomLevel != zoomLevel)
                {
                    return;
                }

         //之前没下载过,需要创建新的VeTile对象。CreateVeTile方法很重要
                VeTile newVeTile = CreateVeTile(drawArgs, row, col, zoomLevel, alMetadata);
                newVeTile.IsNeeded = true;
                lock (veTiles.SyncRoot)
                {
                    veTiles.Add(newVeTile);
                }
            }
        }

CreateVeTile()方法实现,相当底层啦!我自己研究了很长时间,希望能给大家说明白。即使不明白,知道是干啥知道咋用就行啦,不用深究原理的。

private VeTile CreateVeTile(DrawArgs drawArgs, int row, int col, int zoomLevel, ArrayList alMetadata)
        {

//初始化VeTile对象
            VeTile newVeTile = new VeTile(row, col, zoomLevel);

//metadata
            if (alMetadata != null)
            {
                foreach (string metadata in alMetadata)
                {
                    newVeTile.AddMetaData(metadata);
                }
            }
       //获取纹理图片,即从服务器端下载图片用作纹理(重点,稍后详细分析)
            //thread to download new tile(s) or just load from cache
            newVeTile.GetTexture(drawArgs, pixelsPerTile);

//handle the diff projection

      //每个像素代表的距离
            double metersPerPixel = MetersPerPixel(zoomLevel);

        //获取当前级别总的网格边数
            double totalTilesPerEdge = Math.Pow(2, zoomLevel);

//总长度(我认为:就是地球周长)
            double totalMeters = totalTilesPerEdge * pixelsPerTile * metersPerPixel;

//(我认为:halfMeters就是地球周长的一半)
            double halfMeters = totalMeters / 2;

//do meters calculation in VE space
            //the 0,0 origin for VE is in upper left

//在VE空间坐标系下,计算距离,原点在左上角

       //首先求出WW坐标系下的,N、W(这里就是上面求行列数的逆运算啦)
            double N = row * (pixelsPerTile * metersPerPixel);
            double W = col * (pixelsPerTile * metersPerPixel);
            //now convert it to +/- meter coordinates for Proj.4
            //the 0,0 origin for Proj.4 is 0 lat, 0 lon
            //-22 to 22 million, -11 to 11 million

//在以经纬度为0 0为原点的坐标系下的新的N W,坐标系方向轴为 N——》S; W——》E      
            N = halfMeters - N;
            W = W - halfMeters;

//计算出单元格的 E S
            double E = W + (pixelsPerTile * metersPerPixel);
            double S = N - (pixelsPerTile * metersPerPixel);
       

       //给新的瓦片单元格的UL UR LL LR赋值,其实就是单元网格的四角的坐标(理解这点很重要,是为了创建Mesh用)
            newVeTile.UL = new UV(W, N);
            newVeTile.UR = new UV(E, N);
            newVeTile.LL = new UV(W, S);
            newVeTile.LR = new UV(E, S);

//create mesh
            byte opacity = this.Opacity; //from RenderableObject
            float verticalExaggeration = World.Settings.VerticalExaggeration;

//重点CreateMesh()方法,里面也涉及数学,不是太容易理解的
            newVeTile.CreateMesh(opacity, verticalExaggeration);
            newVeTile.CreateDownloadRectangle(drawArgs, World.Settings.DownloadProgressColor.ToArgb());

return newVeTile;
        }

VeTile类,是真正实现渲染方法,也主要用于球体和平面坐标的转换处理、计算,算是VE插件的核心啦,这里面涉及大量的数学知识,主要编程为Direct3D编程。我们现在来看看CreateMesh()方法。

注:如果你看懂CreateMesh(),就说明你已经理解VE大部分啦

   //NOTE this is a mix from Mashi's Reproject and WW for terrain
        public void CreateMesh(byte opacity, float verticalExaggeration)
        {
            this.vertEx = verticalExaggeration;

int opacityColor = System.Drawing.Color.FromArgb(opacity, 0, 0, 0).ToArgb();

meshPointCount = 32; //64; //96 // How many vertices for each direction in mesh (total: n^2)
            //vertices = new CustomVertex.PositionColoredTextured[meshPointCount * meshPointCount];

// Build mesh with one extra row and col around the terrain for normal computation and struts
            vertices = new CustomVertex.PositionNormalTextured[(meshPointCount + 2) * (meshPointCount + 2)];

int upperBound = meshPointCount - 1;
            float scaleFactor = (float)1 / upperBound;
            //using(Projection proj = new Projection(m_projectionParameters))
            //{
            double uStep = (UR.U - UL.U) / upperBound;
            double vStep = (UL.V - LL.V) / upperBound;
            UV curUnprojected = new UV(UL.U - uStep, UL.V + vStep);
       //将平面坐标投影转换为WW球面坐标
            // figure out latrange (for terrain detail)
            UV geoUL = _proj.Inverse(m_ul);
            UV geoLR = _proj.Inverse(m_lr);
            double latRange = (geoUL.U - geoLR.U) * 180 / Math.PI;
       //将弧度转为角度(这里没再使用下面四个变量,原方法用到啦,不研究!)
            North = geoUL.V * 180 / Math.PI;
            South = geoLR.V * 180 / Math.PI;
            West = geoUL.U * 180 / Math.PI;
            East = geoLR.U * 180 / Math.PI;
       //半径
            float meshBaseRadius = (float)_layerRadius;

UV geo;
            Vector3 pos;
            double height = 0;
            for (int i = 0; i < meshPointCount + 2; i++)
            {
                for (int j = 0; j < meshPointCount + 2; j++)
                { //将平面坐标(proj 4)投影转换为WW球面坐标
                    geo = _proj.Inverse(curUnprojected);
                   //将弧度转为角度
                    // Radians -> Degrees
                    geo.U *= 180 / Math.PI;
                    geo.V *= 180 / Math.PI;

if (_terrainAccessor != null)
                    {
                        if (_veForm.IsTerrainOn == true)
                        {
                            //height = heightData[i, j] * verticalExaggeration;
                            //original : need to fetch altitude on a per vertex basis (in VE space) to have matching tile borders (note PM)
                            height = verticalExaggeration * _terrainAccessor.GetElevationAt(geo.V, geo.U, Math.Abs(upperBound / latRange));
                        }
                        else
                        {
                            height = 0;
                        }
                    }
            //将空间坐标转换为笛卡尔坐标
                    pos = MathEngine.SphericalToCartesian(
                        geo.V,
                        geo.U,
                        _layerRadius + height);

//构建Mesh顶点集合
                    int idx = i * (meshPointCount + 2) + j;
                    vertices[idx].X = pos.X;
                    vertices[idx].Y = pos.Y;
                    vertices[idx].Z = pos.Z;
                    //double sinLat = Math.Sin(geo.V);
                    //vertices[idx].Z = (float) (pos.Z * sinLat);

vertices[idx].Tu = (j - 1) * scaleFactor;
                    vertices[idx].Tv = (i - 1) * scaleFactor;
                    //vertices[idx].Color = opacityColor;
                    curUnprojected.U += uStep;
                }
                curUnprojected.U = UL.U - uStep;
                curUnprojected.V -= vStep;
            }
            //}
//构建索引集合,分组存放Mesh点集合中的点。学Direct3D编程,看懂下面的很容易的
            int slices = meshPointCount + 1;
            indices = new short[2 * slices * slices * 3];
            for (int i = 0; i < slices; i++)
            {
                for (int j = 0; j < slices; j++)
                {
                    indices[(2 * 3 * i * slices) + 6 * j] = (short)(i * (meshPointCount + 2) + j);
                    indices[(2 * 3 * i * slices) + 6 * j + 1] = (short)((i + 1) * (meshPointCount + 2) + j);
                    indices[(2 * 3 * i * slices) + 6 * j + 2] = (short)(i * (meshPointCount + 2) + j + 1);

indices[(2 * 3 * i * slices) + 6 * j + 3] = (short)(i * (meshPointCount + 2) + j + 1);
                    indices[(2 * 3 * i * slices) + 6 * j + 4] = (short)((i + 1) * (meshPointCount + 2) + j);
                    indices[(2 * 3 * i * slices) + 6 * j + 5] = (short)((i + 1) * (meshPointCount + 2) + j + 1);
                }
            }

// Compute normals and fold struts
            calculate_normals();
            fold_struts(false, meshBaseRadius);
        }

  (未完待续……)

  

转载于:https://www.cnblogs.com/wuhenke/archive/2009/12/24/1631739.html

WorldWind学习系列十一:Virtual Earth插件学习相关推荐

  1. 大数据学习系列:Hadoop3.0苦命学习(七)

    传送门: 大数据学习系列:Hadoop3.0苦命学习(一) 大数据学习系列:Hadoop3.0苦命学习(二) 大数据学习系列:Hadoop3.0苦命学习(三) 大数据学习系列:Hadoop3.0苦命学 ...

  2. 大数据学习系列:Hadoop3.0苦命学习(一)

    传送门: 大数据学习系列:Hadoop3.0苦命学习(一) 大数据学习系列:Hadoop3.0苦命学习(二) 大数据学习系列:Hadoop3.0苦命学习(三) 大数据学习系列:Hadoop3.0苦命学 ...

  3. 大数据学习系列:Hadoop3.0苦命学习(五)

    传送门: 大数据学习系列:Hadoop3.0苦命学习(一) 大数据学习系列:Hadoop3.0苦命学习(二) 大数据学习系列:Hadoop3.0苦命学习(三) 大数据学习系列:Hadoop3.0苦命学 ...

  4. Hadoop学习系列之Hadoop、Spark学习路线(很值得推荐)

    Hadoop学习系列之Hadoop.Spark学习路线(很值得推荐) 文章出自:http://www.cnblogs.com/zlslch/p/5448857.html 1 Java基础: 视频方面: ...

  5. 元强化学习系列(1)之:元学习入门基础

    元强化学习三境界 统计学是人工智能开始发展的一个基础,古老的人们从大量的数据中发现七所存在的规律,在以统计学为基础的 机器学习(machine learning)时代,复杂一点的分类问题效果就不好了, ...

  6. 【Python学习系列十一】Python实现决策树实现C4.5(信息增益率)

    C4.5是基于ID3改进的分类决策树算法,特点是C4.用信息增益率来选择属性,而ID3使用的是熵(entropy, 熵是一种不纯度度量准则),且对非离散数据也能处理,能够对不完整数据进行处理. 1.信 ...

  7. Java学习系列(十一)Java面向对象之I/O流(下)

    今天接着昨天的IO流讲,内容可能会比较多. DataInputStream与DataOutputStream 它们是建立在已有的IO的基础上的两个特殊的过滤流.规律:它们只是增加了一些特定的方法读取特 ...

  8. 深度学习(十一)RNN入门学习

    RNN入门学习 原文地址:http://blog.csdn.net/hjimce/article/details/49095371 作者:hjimce 一.相关理论 RNN(Recurrent Neu ...

  9. 深度强化学习系列: 最全深度强化学习资料

    关于这项工作: 本工作是一项由深度强化学习实验室(Deep Reinforcement Learning Laboratory, DeepRL-Lab)发起的项目. 文章同步于Github仓库: ht ...

最新文章

  1. csv科学计数法变为普通数字_数据科学家常遇到的10个错误
  2. mysql日期时间函数
  3. (翻译)Quartz官方教程——第七课:TriggerListeners 和 JobListeners
  4. 详解程序包管理RPM
  5. 4.STM32中对USART1_Config()函数的理解(自定义)
  6. PsSetCreateProcessNotifyRoutineEx进程监控框架
  7. CTF(Pwn) 当题目为我们提供Libc版本.so文件, 与 不提供的区别
  8. 剧本翻译之SHUFFLE 6月24日
  9. 计算机支持的游戏化教学,初中信息技术教学中游戏化教学的应用探究
  10. spring核心包功能解析
  11. Kafka : 查看kafka topic的消息offset范围
  12. 区块链+电子档案解决方案
  13. 互联网+脑科学,中国脑计划的机会
  14. 论文笔记--用于人体姿势估计的深度双连续网络(Deep Dual Consecutive Network for Human Pose Estimation)
  15. word 目录 有一部分无无虚线,解决办法
  16. wps的linux文字显示模糊,wps电脑打开文字模糊怎么办
  17. 买的鱼丸怎么做好吃 鱼丸的家常做法介绍
  18. 黑苹果电源键睡眠关机的分析及解决办法
  19. python atm作业详解_Python小案例--ATM系统
  20. 重置 Mac 上的系统管理控制器 (SMC)

热门文章

  1. 阿里云ECS服务器开放端口号
  2. 【详细】AS400系统安装gcc教程
  3. 学习AS400艰难的历程
  4. 多所大学给学生发疫情补助金,最高每人2000元!
  5. Guitar Pro7最新版发布 吉他编谱首选
  6. 怎么找到ADC0804的芯片封装
  7. 大家真的懂退耦电容和旁路电容吗?
  8. 使用计算机打印汉字文档是汉字(),浙江财经学院本科社会调查汇报撰写规范.doc...
  9. 麦当劳爱上热搜,看看这个“营销鬼才”如何玩转创意营销
  10. IT 攻城狮必备的十大算法思想