深入解析ArcGIS Server二次开发之SOE开发

2023-11-06 00:30

本文主要是介绍深入解析ArcGIS Server二次开发之SOE开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一般情况下,一个ArcGIS Engine 工程师转向Web端开发,如果没有相关的web思想,很难转换这个角色,但是如果你有ArcGIS Engine的开发经验,势必会对Web API的开发有很大的帮助,至少今天的主题需要你有ArcGIS Engine开发的经验

 

         Web API 已经提供了足够多个功能供开发者去完善相关的业务功能,特别是GP Service的加入,让Web API在分析方面如虎添翼,但是不可否认,Web技术由于受到网络的限制,浏览器的限制,一些大数据量的分析可能让Web开发者比较挠头,而且如果想实现目前WebAPI还没有提供的功能,基本上无路可走,但是幸好,我们可以使用ArcObject的资源来完善我们的Web应用,也就是ArcGIS for Server的SOE的扩展。

 

参考文档

 

ArcGIS for Server的SOE扩展:http://resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/#/What_is_a_server_object_extension/0001000000zv000000/

关于GP和SOE 的对比问题

         这方面不是本博客的主题,但是有必要简单给大家进行说明一下:

1.GP适合复杂业务流程,较低并发下的应用 

2.SOE适合单一功能的模块,较高并发下的应用

 

SOE开发环境

        ArcObject10.1、VS2010(AO支持的版本)、ArcGIS10.1 for Server、ArcSDE10.1、ArcGIS for JS 3.9

开发目的

         一个地图服务,该地图服务包含一个subway的点图层,该图层有一个name字段,存储相关的地铁站名称,比如东直门站、北京站,使用一个查询条件获得某一个点要素,然后再进行缓冲区(半径1000),然后进行叠加分析,获得该缓冲区下所有的地铁站要素信息,在web端进行渲染。

         当然该需求不管通过ArcGIS for JS基础API或者GP Service都可以实现,但是本次主题的目的就是通过这个例子来了解一下ArcGIS for Server的SOE扩展的开发流程。

 

开发思路

1:首先我们需要进行核心需求的ArcObject代码编程实现,也就是获得要素类对象,然后根据用户提供的属性条件过滤,获得相关要素,然后进行buffer,然后再对该要素类进行空间查询获得符合条件的数据,其实这个熟悉AO开发的是一个非常简单的功能,但是如果希望进行SOE扩展就需要了解SOE开发的相关问题

 

2:在VS上创建SOE程序,系统会自动生成一个相关模板,我们以开放REST为例

 

生成完毕之后,我们就可以看到相关的模板代码。

为了便于用户了解SOE的生命周期和对应接口,我觉得先给大家介绍SOE的部署和调试,如果我们会使用调试,那么我们就可以根据相关模板代码,一步步的进行了解SOE是怎样一个相关原理。

SOE部署

         我们生成了相关的SOE版本,在ArcGIS 10.2版本,支持了影像服务的扩展,所以在我们需要在模板里面输入支持地图服务还是影像服务。如下所示“MapServer”。


 

 [ServerObjectExtension("MapServer",AllCapabilities = "",DefaultCapabilities = "",Description = "",DisplayName = "RestSOE2",Properties = "",SupportsREST = true,SupportsSOAP = false)]

         我们需要在初始化函数Init里面添加如下代码,才能进入调试状态
 

public void Init(IServerObjectHelper pSOH){//生命周期开始时调试System.Diagnostics.Debugger.Launch();serverObjectHelper = pSOH;}

         生成解决方案,在bin目录下可以看到以.soe后缀名的文件。

PS:该.soe后缀的文件可以将.soe名称修改为.zip,那么用户可以看到相关dll以及元数据xml文件信息。

 

打开Manger,点击site选项,在GIS Server页选择Extensions

 

         然后再Services选项中,选择某个服务比如JS 地图服务,在该服务的Capablities选择新的RestSOE2扩展,然后保存和重新启动服务即可。

 

SOE调试

         如果前面在init函数中已经添加了调试代码,那么在该服务重启过程中就会弹出如下对话框

 

 

         选择相应的SOE 程序点击是,那么系统就会进入调试状态

 

 

         掌握了SOE的调试,用户可以跟踪相关的调试顺序,了解SOE的具体执行步骤。

注意:

1:我的VS、AO、ArcGIS  Server都在同一个环境下,相关调试没有问题,但是如果VS和Server不再一台机器上,调用调试可能会有问题,咨询相关人员回答在不同环境下是可以调试的,但是我并没有解决。

2:因为64Bit的关系,代码不能像C#+ArcGIS Engine 开发可以实时修改代码进行调试

3:SOE的代码如果需要修改,停止调试,然后修改代码,然后重新生成SOE,然后删除manager的扩展项,然后添加新修改的soe文件,然后再针对某个服务添加扩展重新启动服务,这个就比较麻烦,建议现在纯AO环境下进行调试,这样会减少比较多的麻烦。

 

SOE生命周期

通过跟踪代码,我们可以了解SOE的生命周期,建议用户还需要提前查看相关帮助

                   ============在添加扩展SOE服务的重启过程============

 

1:MapServer初始化

2:SOE初始化
 

public void Init(IServerObjectHelper pSOH){System.Diagnostics.Debugger.Launch();serverObjectHelper = pSOH;}

3:MapServer启动

4:SOE构造

public void Construct(IPropertySet props){configProps = props;}

 

         ===============生成相关的SOE服务连接,点击该连接=====

5:SOE活动——REST/SOAP处理请求——SOE停止活动

这是一个相关循环的过程。

         在点击链接时会创建Schema
 

 public string GetSchema(){return reqHandler.GetSchema();}

         然后进行REST/SOAP处理请求

 public byte[] HandleRESTRequest(string Capabilities, string resourceName, string operationName, string operationInput, string outputFormat, string requestProperties, out string responseProperties){return reqHandler.HandleRESTRequest(Capabilities, resourceName, operationName, operationInput, outputFormat, requestProperties, out responseProperties);}

 

         请求完毕之后,创建Root资源,也就是可以看到的SOE介绍信息

 

 

private byte[] RootResHandler(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties){responseProperties = null;JsonObject result = new JsonObject();result.AddString("hello", "world");return Encoding.UTF8.GetBytes(result.ToJson());}

         点击相关的sampleOperation函数,我们输入相关参数值,点击GET请求

 

 

         我们可以看到系统还会进行REST/SOAP请求,然后进入相关的主体函数中,核心的代码应该在该函数中编写即可

 private byte[] SampleOperHandler(NameValueCollection boundVariables,JsonObject operationInput,string outputFormat,string requestProperties,out string responseProperties){responseProperties = null;string parm1Value;bool found = operationInput.TryGetString("parm1", out parm1Value);if (!found || string.IsNullOrEmpty(parm1Value))throw new ArgumentNullException("parm1");string parm2Value;found = operationInput.TryGetString("parm2", out parm2Value);if (!found || string.IsNullOrEmpty(parm2Value))throw new ArgumentNullException("parm2");JsonObject result = new JsonObject();result.AddString("parm1", parm1Value);result.AddString("parm2", parm2Value);return Encoding.UTF8.GetBytes(result.ToJson());}

6:MapServer停止

7:SOE关闭
 

 public void Shutdown(){}

关闭之后需要用户释放相关的对象资源。

 

关于Schema

 

         一般情况下,我们实现一个功能都会需要输入相关参数,和参与该功能的图层或者字段等,那么我们将Schema称之为SOE中的资源和操作的层次结构,资源(resource)就是从服务器端返回的信息块,比如图层列表、缓存服务的比例尺级别,如果这次实验用的的图层名称subway,字段名称name,操作(operations)就是让服务器资源做的一些事情,比如我们设置的where条件以及缓冲区bufferRedius等

 private RestResource CreateRestSchema(){RestResource rootRes = new RestResource(soe_name, false, RootResHandler);RestOperation sampleOper = new RestOperation("QueryBuffer",new string[] { "WhereClause", "BufferRadius" },new string[] { "json" },DoQueryBuffer);rootRes.operations.Add(sampleOper);RestResource propertiesResource = new RestResource("properties", false, PropertiesResHandler);rootRes.resources.Add(propertiesResource);return rootRes;}

 

关于SOE的处理流程

 

1:客户端请求参数,请求参数反序列化

 

2:SOE业务逻辑实现处理

 

3:结果序列化

 

         关于参数的序列化或者反序列化,都使用了ESRI.ArcGIS.SOESupport;

 

序列化 

 

 反序列化

 

         我个人觉得一句话总结,一切皆JSON。最终返回的结果都是JSON格式即可。

 

         比如本次主题的目的就是查询出来符合条件和缓冲区面积的点要素几何,那么我们需要获得这些相关Geometry对象的JSON格式的集合即可。


 

private byte[] GetResultJson(List<IFeature> pList){List<JsonObject> pJoList = null;if (pList!=null){IFeature pfea = null;pJoList = new List<JsonObject>();for (int i = 1; i < pList.Count; i++){pfea = pList[i];pJoList.Add(Conversion.ToJsonObject(pfea.ShapeCopy));}}JsonObject resultJson = new JsonObject();resultJson.AddArray("geometries", pJoList.ToArray());byte[] result = Encoding.UTF8.GetBytes(resultJson.ToJson());return result;}

关于获取数据源信息

 

         SOE开发与AO开发不一样的地方,AO可以直接获得SDE连接对象,然后打开获得相关的IFeatureClass对象等,那么SOE跟这个不太一样,它需要通过服务来获得对象,主要关注IMapServerDataAccess获得相关的MSD数据,有两个功能可以使用

 

一般情况下使用GetDataSource,那么如果有关联关系的可以使用GetDisplayDataSouce。

 //获取数据             IMapServer3 mapServer = (IMapServer3)serverObjectHelper.ServerObject;IMapLayerInfo layerInfo;IMapLayerInfos layerInfos = mapServer.GetServerInfo(mapServer.DefaultMapName).MapLayerInfos;// 获取查询图层idint layercount = layerInfos.Count;int layerIndex = 0;string sName = "";for (int i = 0; i < layercount; i++){layerInfo = layerInfos.get_Element(i);//默认的oracle数据库的sde用户,查询出来的layerInfo.Name为sde.subway所以需要去掉sde.信息sName=layerInfo.Name;if (sName != ""){sName= sName.Substring(sName.IndexOf(".") + 1, (sName.Length - sName.IndexOf(".")) - 1).Trim();}if (sName == m_LayerName){layerIndex = i;break;}}IMapServerDataAccess dataAccess = (IMapServerDataAccess)mapServer;this.m_FeatureClass = (IFeatureClass)dataAccess.GetDataSource(mapServer.DefaultMapName, layerIndex);

 

相关SOE源代码
 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;using System.Collections.Specialized;using System.Runtime.InteropServices;using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Server;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.SOESupport;//TODO: sign the project (project properties > signing tab > sign the assembly)
//      this is strongly suggested if the dll will be registered using regasm.exe <your>.dll /codebasenamespace RestSOE1
{[ComVisible(true)][Guid("99611947-9d7d-4f6f-a4c4-d835ce4a7fa5")][ClassInterface(ClassInterfaceType.None)][ServerObjectExtension("MapServer",//use "MapServer" if SOE extends a Map service and "ImageServer" if it extends an Image service.AllCapabilities = "",DefaultCapabilities = "",Description = "Query Buffer SOE",DisplayName = "RestSOE1",Properties = "Field_Name=name;Layer_Name=subway",SupportsREST = true,SupportsSOAP = false)]public class RestSOE1 : IServerObjectExtension, IObjectConstruct, IRESTRequestHandler{private string soe_name;private IPropertySet configProps;private IServerObjectHelper serverObjectHelper;private ServerLogger logger;private IRESTRequestHandler reqHandler;private IFeatureClass m_FeatureClass = null;//查询图层名称private string m_LayerName = "";//查询字段名称private string m_FieldName = "";public RestSOE1(){soe_name = this.GetType().Name;logger = new ServerLogger();reqHandler = new SoeRestImpl(soe_name, CreateRestSchema()) as IRESTRequestHandler;}#region IServerObjectExtension Memberspublic void Init(IServerObjectHelper pSOH){//生命周期开始时调试System.Diagnostics.Debugger.Launch();serverObjectHelper = pSOH;}public void Shutdown(){this.soe_name = null;this.m_FeatureClass = null;this.logger = null;this.m_FieldName = null;this.m_LayerName = null;this.serverObjectHelper = null;this.configProps = null;this.reqHandler = null;}#endregion#region IObjectConstruct Memberspublic void Construct(IPropertySet props){configProps = props;if (props.GetProperty("Field_Name") != null){this.m_FieldName = props.GetProperty("Field_Name") as string;}else {throw new ArgumentNullException();}if (props.GetProperty("Layer_Name") != null){this.m_LayerName = props.GetProperty("Layer_Name") as string;}else{throw new ArgumentNullException();}try{//获取数据              IMapServer3 mapServer = (IMapServer3)serverObjectHelper.ServerObject;IMapLayerInfo layerInfo;IMapLayerInfos layerInfos = mapServer.GetServerInfo(mapServer.DefaultMapName).MapLayerInfos;// 获取查询图层idint layercount = layerInfos.Count;int layerIndex = 0;string sName = "";for (int i = 0; i < layercount; i++){layerInfo = layerInfos.get_Element(i);//默认的oracle数据库的sde用户,查询出来的layerInfo.Name为sde.subway所以需要去掉sde.信息sName=layerInfo.Name;if (sName != ""){sName= sName.Substring(sName.IndexOf(".") + 1, (sName.Length - sName.IndexOf(".")) - 1).Trim();}if (sName == m_LayerName){layerIndex = i;break;}}IMapServerDataAccess dataAccess = (IMapServerDataAccess)mapServer;this.m_FeatureClass = (IFeatureClass)dataAccess.GetDataSource(mapServer.DefaultMapName, layerIndex);if (this.m_FeatureClass == null){logger.LogMessage(ServerLogger.msgType.error, "Construct", 8000, "SOE custom error: Layer name not found.");return;}if (this.m_FeatureClass.FindField(this.m_FieldName) == -1){logger.LogMessage(ServerLogger.msgType.error, "Construct", 8000, "SOE custom error: Field not found in layer.");}}catch (Exception e){ }}#endregion#region IRESTRequestHandler Memberspublic string GetSchema(){return reqHandler.GetSchema();}public byte[] HandleRESTRequest(string Capabilities, string resourceName, string operationName, string operationInput, string outputFormat, string requestProperties, out string responseProperties){return reqHandler.HandleRESTRequest(Capabilities, resourceName, operationName, operationInput, outputFormat, requestProperties, out responseProperties);}#endregionprivate RestResource CreateRestSchema(){RestResource rootRes = new RestResource(soe_name, false, RootResHandler);RestOperation sampleOper = new RestOperation("QueryBuffer",new string[] { "WhereClause", "BufferRadius" },new string[] { "json" },DoQueryBuffer);rootRes.operations.Add(sampleOper);RestResource propertiesResource = new RestResource("properties", false, PropertiesResHandler);rootRes.resources.Add(propertiesResource);return rootRes;}private byte[] PropertiesResHandler(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties){responseProperties = "{\"Content-Type\" : \"application/json\"}";JsonObject result = new JsonObject();result.AddString("Field_Name", this.m_FieldName);result.AddString("Layer_Name", this.m_LayerName);return Encoding.UTF8.GetBytes(result.ToJson());}private byte[] RootResHandler(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties){responseProperties = null;JsonObject result = new JsonObject();result.AddString("名称", "查询缓冲Soe");result.AddString("描述", "通过属性查询条件,获得一个点要素,然后再做缓冲区");result.AddString("方法", "条件过滤加上缓冲区");return Encoding.UTF8.GetBytes(result.ToJson());}private byte[] DoQueryBuffer(NameValueCollection boundVariables,JsonObject operationInput,string outputFormat,string requestProperties,out string responseProperties){responseProperties = null;string WhereClause;bool found = operationInput.TryGetString("WhereClause", out WhereClause);if (!found || string.IsNullOrEmpty(WhereClause))throw new ArgumentNullException("WhereClause");double? BufferRadius;found = operationInput.TryGetAsDouble("BufferRadius", out BufferRadius);if (!found || !BufferRadius.HasValue)throw new ArgumentNullException("BufferRadius");List<IFeature> pList = GetQueryBufferGeometryList(this.m_FieldName, WhereClause, BufferRadius);return GetResultJson(pList);}private IGeometry GetQueryBufferGeometry(string sFieldName, IFeatureClass pfc,string sWhereClause, object sBufferRadius){IGeometry pGeo = null;IQueryFilter qf = new QueryFilterClass();//这里只测试字符串查询,其他类型不再进行逻辑检查和编写string sWhere=sFieldName+"='"+sWhereClause+"'";qf.WhereClause = sWhere;IFeatureCursor pCur= pfc.Search(qf, true);IFeature pFea = pCur.NextFeature();ITopologicalOperator pTO = null; while (pFea != null){pTO = pFea.ShapeCopy as ITopologicalOperator;pGeo = pTO.Buffer(Convert.ToDouble(sBufferRadius));pFea = pCur.NextFeature();}return pGeo;}private List<IFeature> GetQueryBufferGeometryList(string sFieldName, string sWhereClause, object sBufferRadius){List<IFeature> pFeatureList = new List<IFeature>();IQueryFilter qf = new QueryFilterClass();//这里只测试字符串查询,其他类型不再进行逻辑检查和编写string sWhere = sFieldName + "='" + sWhereClause + "'";qf.WhereClause = sWhere;IFeatureCursor pCur = this.m_FeatureClass.Search(qf, false);IFeature pFea = pCur.NextFeature();ITopologicalOperator pTO = null;IGeometry pBufferGeo = null;while (pFea != null){pTO = pFea.ShapeCopy as ITopologicalOperator;pBufferGeo = pTO.Buffer(Convert.ToDouble(sBufferRadius));pFea = pCur.NextFeature();}ISpatialFilter sf = new SpatialFilterClass();sf.Geometry = pBufferGeo;sf.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;sf.GeometryField = this.m_FeatureClass.ShapeFieldName;ISpatialReference sr=(this.m_FeatureClass as IGeoDataset).SpatialReference;sf.set_OutputSpatialReference(this.m_FeatureClass.ShapeFieldName, sr);IFeatureCursor pFCur = this.m_FeatureClass.Search(sf, false);while ((pFea = pFCur.NextFeature()) != null){pFeatureList.Add(pFea);}return pFeatureList;}private byte[] GetResultJson(List<IFeature> pList){List<JsonObject> pJoList = null;if (pList!=null){IFeature pfea = null;pJoList = new List<JsonObject>();for (int i = 1; i < pList.Count; i++){pfea = pList[i];pJoList.Add(Conversion.ToJsonObject(pfea.ShapeCopy));}}JsonObject resultJson = new JsonObject();resultJson.AddArray("geometries", pJoList.ToArray());byte[] result = Encoding.UTF8.GetBytes(resultJson.ToJson());return result;}}
}

 

         很久不写Engine代码了,连很简单的东西都忘记的差不多了。我们可以通过前面介绍的调试方法,测试是否可以获得正确的结果?

 

 

ArcGIS for JS 调用SOE服务

 

关于ArcGIS for JS调用SOE 服务主要使用了如下代码:


 

var WhereClause = dom.byId("WhereClause").value;var BufferRadius = dom.byId("BufferRadius").value;var content = {'WhereClause': WhereClause,'BufferRadius': BufferRadius,'f': "json"};var Request = esriRequest({url: soeUrl,content: content,handleAs: "json",callbackParamName: "callback"});Request.then(function (responses) {...........});

 

使用esriRequest,传入soe的REST,然后将属性参数传入(content),然后对请求进行枚举查询,由于我们SOE获得的结果是查询的IGeometry的Array,那么执行对返回结果进行循环获得,然后将相关结果作为一个graphic添加到map即可。

 

 

 

ArcGIS for JS调用SOE 源代码如下

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" /><title>Simple Map</title><link rel="stylesheet" type="text/css" href="http://localhost/arcgis_js_v39_api/arcgis_js_api/library/3.9/3.9/js/dojo/dijit/themes/tundra/tundra.css" /><link rel="stylesheet" type="text/css" href="http://localhost/arcgis_js_v39_api/arcgis_js_api/library/3.9/3.9/js/esri/css/esri.css" /><script type="text/javascript" src="http://localhost/arcgis_js_v39_api/arcgis_js_api/library/3.9/3.9"> </script><script type="text/javascript" src="jsapi_vsdoc_v32_2012.js"></script><style>html, body, #mapDiv {padding: 0;margin: 0;height: 100%;}#messages {background-color: #fff;box-shadow: 0 0 5px #888;font-size: 1.1em;max-width: 15em;padding: 0.5em;position: absolute;right: 20px;top: 300px;z-index: 40;}</style><script>var map;require(["esri/map", "esri/layers/FeatureLayer","esri/layers/ArcGISDynamicMapServiceLayer","esri/request","esri/graphic", "esri/symbols/SimpleMarkerSymbol","esri/geometry/Point","esri/SpatialReference","esri/config", "esri/Color", "dojo/_base/array", "dojo/on", "dojo/dom", "dojo/domReady!"], function (Map, FeatureLayer, ArcGISDynamicMapServiceLayer,esriRequest,Graphic, SimpleMarkerSymbol,Point,SpatialReference,esriConfig, Color,arrayUtils,on, dom) {// use a proxy page if a URL generated by this page is greater than 2000 characters//// this should not be needed as nearly all query & select functions are performed on the clientesriConfig.defaults.io.proxyUrl = "/proxy";map = new Map("mapDiv", {});var mapUrl = "http://192.168.220.125:6080/arcgis/rest/services/JS/MapServer";var soeUrl = "http://192.168.220.125:6080/arcgis/rest/services/JS/MapServer/exts/RestSOE1/QueryBuffer";//Takes a URL to a non cached map service.var dynamicMapServiceLayer = new ArcGISDynamicMapServiceLayer(mapUrl);map.addLayer(dynamicMapServiceLayer);on(dom.byId("execute"), "click", function () {var WhereClause = dom.byId("WhereClause").value;var BufferRadius = dom.byId("BufferRadius").value;var content = {'WhereClause': WhereClause,'BufferRadius': BufferRadius,'f': "json"};var Request = esriRequest({url: soeUrl,content: content,handleAs: "json",callbackParamName: "callback"});Request.then(function (responses) {map.graphics.clear();var markerSymbol = new SimpleMarkerSymbol();markerSymbol.setPath("M16,4.938c-7.732,0-14,4.701-14,10.5c0,1.981,0.741,3.833,2.016,5.414L2,25.272l5.613-1.44c2.339,1.316,5.237,2.106,8.387,2.106c7.732,0,14-4.701,14-10.5S23.732,4.938,16,4.938zM16.868,21.375h-1.969v-1.889h1.969V21.375zM16.772,18.094h-1.777l-0.176-8.083h2.113L16.772,18.094z");markerSymbol.setColor(new Color("#00FFFF"));markerSymbol.setSize(30);markerSymbol.setOffset(20, 10);var wgs = new SpatialReference({"wkid": 102100});for (var i = 0; i < responses.geometries.length; i++) {var pt = new Point(responses.geometries[i].x, responses.geometries[i].y, wgs);var graphic = new Graphic(pt, markerSymbol);map.graphics.add(graphic);}});});});</script></head><body>查询条件<input type="text" id="WhereClause" value="北京站">缓冲区半径<input type="text" id="BufferRadius" value="1500"><input id="execute" type="button" value="执行查询"><div id="mapDiv"></div></body></html>

 

获得的查询页面和结果如下

 

欢迎关注微信公众号“GIS带我奔跑”获取更多技术文章。

 

 

 

 

 

 

 

原文链接:https://blog.csdn.net/linghe301/article/details/38434469

这篇关于深入解析ArcGIS Server二次开发之SOE开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/353431

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta