Geoserver源码解读五 Catalog

系列文章目录

Geoserver源码解读一 环境搭建

Geoserver源码解读二 主入口

Geoserver源码解读三 GeoServerBasePage

Geoserver源码解读四 REST服务

Geoserver源码解读五 Catalog


目录

系列文章目录

前言

一、定义

二、前置知识点

1.Spring 的 Bean 生命周期

ApplicationContextAware

BeanPostProcessor 

三、常见用法

四、结构梳理

1.查询工作空间

2.资源读取

五、总结


前言

Catalog数据目录是 Geoserver比较核心的内容,理解Catalog对理解Geoserver的存储机制有很大帮助,夸张点儿说就是,理解了它的逻辑就相当于理解了geoserver的逻辑。本文同样以工作空间为例梳理下它的相关逻辑。


一、定义

GeoServer 的配置形成一个目录,包括工作空间、数据源、图层、样式等。实际上就是一堆文件夹和文本文件(充当的是文件型数据库的作用)。

二、前置知识点

1.Spring 的 Bean 生命周期

Spring 的 Bean 生命周期是指从 Bean 被创建到销毁的整个过程。这个过程包括多个阶段,每个阶段都可以通过不同的方式进行自定义,在 Spring 容器启动时,它会读取配置元数据(如 XML、注解或 Java 配置),解析这些元数据,并将定义的 Bean 注册到容器中。

ApplicationContextAware

Spring 的 ApplicationContextAware 用于标记那些需要知道当前 Spring 应用上下文(ApplicationContext)的 Bean。当一个 Bean 实现 ApplicationContextAware 接口时,它可以在 Bean 的生命周期中访问和操作当前的 ApplicationContext,也就是说它的执行顺序早于Bean 的生命周期

BeanPostProcessor 

Spring 的 BeanPostProcessor 允许你在 Bean 的初始化前后添加自定义逻辑。这个接口定义了两个方法,分别在 Bean 初始化之前和之后被调用。

  • postProcessBeforeInitialization(Object bean, String beanName): 在 Bean 的初始化方法(如 afterPropertiesSet 或自定义的 init-method)之前被调用。
  • postProcessAfterInitialization(Object bean, String beanName): 在 Bean 的初始化方法之后被调用

举个栗子说明

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;public class MyBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware,InitializingBean, DisposableBean {private ApplicationContext applicationContext;// 1.这个最先被执行@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;System.out.println("ApplicationContextAware: Application context is set");}// 2.这个第二被执行@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Before initialization of bean: " + beanName);return bean; // 可以返回原始的 Bean 或包装后的 Bean}// 4. 这个第四@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean: Bean properties are set");}// 3. 这个第三public void initMethod() {System.out.println("Custom init method: Bean is initializing");}// 5. 这个第五public void destroyMethod() {System.out.println("Custom destroy method: Bean is being destroyed");}
}
<bean id="myBean" class="com.example.MyBean" init-method="initMethod" destroy-method="destroyMethod">
</bean>

对Spring Bean生命周期的掌握。方便了解分析Geoserver是怎么对资源目录进行解析的

三、常见用法

 获取目录

Catalog catalog = geoServer.getCatalog();

获取所有工作空间

WorkspaceInfo workspace = catalog.getWorkspaces();

获取所有数据存储

List<DataStoreInfo> dataStores = catalog.getStores( DataStoreInfo.class );

获取特定图层

LayerInfo layer = catalog.getLayer( "myLayer" );

四、结构梳理

1.查询工作空间

查询入口

@RestController
@RequestMapping(path = RestBaseController.ROOT_PATH + "/workspaces",produces = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE,MediaType.TEXT_HTML_VALUE})
public class WorkspaceController extends AbstractCatalogController {@Autowiredpublic WorkspaceController(@Qualifier("catalog") Catalog catalog) {super(catalog);}@GetMappingpublic RestWrapper workspacesGet() {List<WorkspaceInfo> wkspaces = catalog.getWorkspaces();return wrapList(wkspaces, WorkspaceInfo.class);}
}

从上面代码可以看出来catalog是依赖注入进来的

    <bean id="localWorkspaceCatalog" class="org.geoserver.catalog.impl.LocalWorkspaceCatalog"><constructor-arg ref="advertisedCatalog" /></bean><alias name="localWorkspaceCatalog" alias="catalog"/> 

实际用到的Catalog是localWorkspaceCatalog而localWorkspaceCatalog使用了AbstractDecorator技术。这一点上一章的 四、前置知识点-AbstractDecorator 也有讲到,此处不多赘述,本质上就是定义了一个变量去直接操作父类。

public class LocalWorkspaceCatalog extends AbstractCatalogDecorator implements Catalog {public LocalWorkspaceCatalog(Catalog delegate) {super(delegate);}
}

这里同样是个依赖注入,以此类推,下面的三级都是依赖注入

    <bean id="rawCatalog" class="org.geoserver.catalog.impl.CatalogImpl" depends-on="configurationLock"><property name="resourceLoader" ref="resourceLoader"/>  </bean><bean id="secureCatalog" class="org.geoserver.security.SecureCatalogImpl" depends-on="accessRulesDao,extensions"><constructor-arg ref="rawCatalog" /> </bean><bean id="advertisedCatalog" class="org.geoserver.catalog.impl.AdvertisedCatalog"><constructor-arg ref="secureCatalog" /><property name="layerGroupVisibilityPolicy"><bean id="org.geoserver.catalog.LayerGroupVisibilityPolicy.HIDE_NEVER" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/></property></bean>    <bean id="localWorkspaceCatalog" class="org.geoserver.catalog.impl.LocalWorkspaceCatalog"><constructor-arg ref="advertisedCatalog" /></bean>

根据上面代码可以看到,最终的生效的父类Catalog是 rawCatalog 也就是 org.geoserver.catalog.impl.CatalogImpl,从depends-on看出它的前置条件是

<bean id="configurationLock" class="org.geoserver.GeoServerConfigurationLock"/>
先看下这个前置条件我替大家看过了就是一个线程锁,当两个用户同时尝试更改同一设置时,这个锁可以确保它们不会互相覆盖对方的更改。

然后再看org.geoserver.catalog.impl.CatalogImpl里面是怎么查询工作空间的

    @Overridepublic List<WorkspaceInfo> getWorkspaces() {return facade.getWorkspaces();}@Overridepublic WorkspaceInfo getWorkspace(String id) {return facade.getWorkspace(id);}

可以看出来catalog内部又使用了个facade,这层级套用的是真的深啊,静不下心的话根本就看不下去,再继续往下看这个facade

    /** data access facade */protected CatalogFacade facade;public CatalogImpl() {setFacade(new DefaultCatalogFacade(this));dispatcher = new CatalogEventDispatcher();resourcePool = ResourcePool.create(this);}

在构造函数中的给了它一个 DefaultCatalogFacade

src/main/java/org/geoserver/catalog/impl/DefaultCatalogFacade.java

继续查看DefaultCatalogFacade可以看到下面的代码

    /** workspaces */protected CatalogInfoLookup<WorkspaceInfo> workspaces =new CatalogInfoLookup<>(WORKSPACE_NAME_MAPPER);@Overridepublic List<WorkspaceInfo> getWorkspaces() {return ModificationProxy.createList(new ArrayList<>(workspaces.values()), WorkspaceInfo.class);}@Overridepublic WorkspaceInfo getWorkspace(String id) {WorkspaceInfo ws = workspaces.findById(id, WorkspaceInfo.class);return wrapInModificationProxy(ws, WorkspaceInfo.class);}

可以看出来DefaultCatalogFacade下面还有一层workspaces也就是CatalogInfoLookup

src/main/java/org/geoserver/catalog/impl/CatalogInfoLookup.java

这是一个内部工具类,用于在 GeoServer 的目录(catalog)中查找和存储 CatalogInfo 对象。CatalogInfo 是 GeoServer 目录中的一个接口,它代表目录中的一个信息对象,如工作空间(Workspace)、数据存储(DataStore)、覆盖范围(Coverage)等

class CatalogInfoLookup<T extends CatalogInfo> {public T add(T value) {if (Proxy.isProxyClass(value.getClass())) {ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(value);value = (T) h.getProxyObject();}Map<Name, T> nameMap = getMapForValue(nameMultiMap, value);Name name = nameMapper.apply(value);nameMap.put(name, value);Map<String, T> idMap = getMapForValue(idMultiMap, value);return idMap.put(value.getId(), value);}public Collection<T> values() {List<T> result = new ArrayList<>();for (Map<String, T> v : idMultiMap.values()) {result.addAll(v.values());}return result;}
}

从上面代码可以看出来查询出来的实际上是 idMultiMap 它的新增方法是add,到这里整个查询的逻辑基本上已经梳理到头了,但是这个idMultiMap  到底是在什么时候生成的也就是说这个add是在什么时候调用的就又是另外一条业务梳理线了

2.资源读取

上面讲到查询工作目录的时候最终到了CatalogInfoLookup的 idMultiMap ,我在add方法中打了个断点,跟踪了下进程,梳理出了下面的逻辑

首先是入口

src/main/java/applicationContext.xml

当项目起来后读取这个配置文件并初始化

<bean id="geoServerLoader" class="org.geoserver.config.GeoServerLoaderProxy"><constructor-arg ref="resourceLoader"/></bean>

读到配置文件后进入Bean生命周期(上面有讲到)

public class GeoServerLoaderProxyimplements BeanPostProcessor,ApplicationListener<ContextClosedEvent>,ApplicationContextAware,GeoServerReinitializer {/** resource loader */protected GeoServerResourceLoader resourceLoader;/** the actual loader */GeoServerLoader loader;public GeoServerLoaderProxy(GeoServerResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.loader = lookupGeoServerLoader(applicationContext);loader.setApplicationContext(applicationContext);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {if (loader != null) {return loader.postProcessAfterInitialization(bean, beanName);}return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {if (loader != null) {return loader.postProcessBeforeInitialization(bean, beanName);}return bean;}protected GeoServerLoader lookupGeoServerLoader(ApplicationContext appContext) {GeoServerLoader loader = GeoServerExtensions.bean(GeoServerLoader.class, appContext);if (loader == null) {loader = new DefaultGeoServerLoader(resourceLoader);}return loader;}@Overridepublic void initialize(GeoServer geoServer) throws Exception {loader.initializeDefaultStyles(geoServer.getCatalog());}
}

然后重点看一下它的postProcessBeforeInitialization方法

    protected GeoServerLoader lookupGeoServerLoader(ApplicationContext appContext) {GeoServerLoader loader = GeoServerExtensions.bean(GeoServerLoader.class, appContext);if (loader == null) {loader = new DefaultGeoServerLoader(resourceLoader);}return loader;}

实际上就是loader(src/main/java/org/geoserver/config/GeoServerLoader.java)的postProcessBeforeInitialization方法

    public final Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {if (bean instanceof Catalog) {// ensure this is not a wrapper but the real dealif (bean instanceof Wrapper && ((Wrapper) bean).isWrapperFor(Catalog.class)) {return bean;}postProcessBeforeInitializationCatalog(bean);}if (bean instanceof GeoServer) {postProcessBeforeInitializationGeoServer(bean);}return bean;}

再继续看postProcessBeforeInitializationCatalog方法

private void postProcessBeforeInitializationCatalog(Object bean) {// loadtry {// setup ADMIN_ROLE security context to load secured resourcesactivateAdminRole();Catalog catalog = (Catalog) bean;XStreamPersister xp = xpf.createXMLPersister();xp.setCatalog(catalog);loadCatalog(catalog, xp);// initialize stylesinitializeStyles(catalog, xp);} catch (Exception e) {throw new RuntimeException(e);} finally {// clear security contextSecurityContextHolder.clearContext();}}

重点在loadCatalog,再继续往下看loadCatalog

@Overrideprotected void loadCatalog(Catalog catalog, XStreamPersister xp) throws Exception {catalog.setResourceLoader(resourceLoader);readCatalog(catalog, xp);if (!legacy) {// add the listener which will persist changescatalog.addListener(new GeoServerConfigPersister(resourceLoader, xp));catalog.addListener(new GeoServerResourcePersister(catalog));}executeListener(catalog, xp);}

重点在readCatalog,再继续往下看readCatalog

重点来了啊 重点来了

protected void readCatalog(Catalog catalog, XStreamPersister xp) throws Exception {// we are going to synch up the catalogs and need to preserve listeners,// but these two fellas are attached to the new catalog as wellcatalog.removeListeners(ResourcePool.CacheClearingListener.class);catalog.removeListeners(GeoServerConfigPersister.class);catalog.removeListeners(GeoServerResourcePersister.class);// look for catalog.xml, if it exists assume we are dealing with// an old data directoryResource f = resourceLoader.get("catalog.xml");CatalogImpl catalog2;if (!Resources.exists(f)) {// assume 2.x style data directoryStopwatch sw = Stopwatch.createStarted();LOGGER.config("Loading catalog " + resourceLoader.getBaseDirectory());catalog2 = (CatalogImpl) readCatalog(xp);LOGGER.config("Read catalog in " + sw.stop());} else {// import old style catalog, register the persister now so that we start// with a new version of the catalogcatalog2 = (CatalogImpl) readLegacyCatalog(f, xp);}List<CatalogListener> listeners = new ArrayList<>(catalog.getListeners());// make to remove the old resource pool catalog listener((CatalogImpl) catalog).sync(catalog2);// attach back the old listenersfor (CatalogListener listener : listeners) {catalog.addListener(listener);}}
    /** Reads the catalog from disk. */Catalog readCatalog(XStreamPersister xp) throws Exception {CatalogImpl catalog = new CatalogImpl();catalog.setResourceLoader(resourceLoader);xp.setCatalog(catalog);xp.setUnwrapNulls(false);// see if we really need to verify stores on startupboolean checkStores = checkStoresOnStartup(xp);if (!checkStores) {catalog.setExtendedValidation(false);}// global stylesloadStyles(resourceLoader.get("styles"), catalog, xp);// workspaces, stores, and resourcesResource workspaces = resourceLoader.get("workspaces");if (Resources.exists(workspaces)) {// do a first quick scan over all workspaces, setting the defaultResource dws = workspaces.get("default.xml");WorkspaceInfo defaultWorkspace = null;if (Resources.exists(dws)) {try {defaultWorkspace = depersist(xp, dws, WorkspaceInfo.class);if (LOGGER.isLoggable(Level.CONFIG)) {LOGGER.config("Loaded default workspace '" + defaultWorkspace.getName() + "'");}} catch (Exception e) {LOGGER.log(Level.WARNING, "Failed to load default workspace", e);}} else {LOGGER.warning("No default workspace was found.");}List<Resource> workspaceList =workspaces.list().parallelStream().filter(r -> Resources.DirectoryFilter.INSTANCE.accept(r)).collect(Collectors.toList());try (AsynchResourceIterator<WorkspaceContents> it =new AsynchResourceIterator<>(workspaces,Resources.DirectoryFilter.INSTANCE,new WorkspaceMapper())) {while (it.hasNext()) {WorkspaceContents wc = it.next();WorkspaceInfo ws;final Resource workspaceResource = wc.resource;try {ws = depersist(xp, wc.contents, WorkspaceInfo.class);catalog.add(ws);LOGGER.log(Level.CONFIG,() -> String.format("Loaded workspace '%s'", ws.getName()));} catch (Exception e) {LOGGER.log(Level.WARNING,"Failed to load workspace '" + workspaceResource.name() + "'",e);continue;}// load the namespaceNamespaceInfo ns = null;try {ns = depersist(xp, wc.nsContents, NamespaceInfo.class);catalog.add(ns);} catch (Exception e) {LOGGER.log(Level.WARNING,"Failed to load namespace for '" + workspaceResource.name() + "'",e);}// set the default workspace, this value might be null in the case of coming// from a// 2.0.0 data directory. See https://osgeo-org.atlassian.net/browse/GEOS-3440if (defaultWorkspace != null) {if (ws.getName().equals(defaultWorkspace.getName())) {catalog.setDefaultWorkspace(ws);if (ns != null) {catalog.setDefaultNamespace(ns);}}} else {// create the default.xml filedefaultWorkspace = catalog.getDefaultWorkspace();if (defaultWorkspace != null) {try {persist(xp, defaultWorkspace, dws);} catch (Exception e) {LOGGER.log(Level.WARNING,"Failed to persist default workspace '"+ workspaceResource.name()+ "'",e);}}}// load the styles for the workspaceResource styles = workspaceResource.get("styles");if (styles != null) {loadStyles(styles, catalog, xp);}}}// maps each store into a SingleResourceContentsResourceMapper<SingleResourceContents> storeMapper =sd -> {Resource f = sd.get("datastore.xml");if (Resources.exists(f)) {return new SingleResourceContents(f, f.getContents());}f = sd.get("coveragestore.xml");if (Resources.exists(f)) {return new SingleResourceContents(f, f.getContents());}f = sd.get("wmsstore.xml");if (Resources.exists(f)) {return new SingleResourceContents(f, f.getContents());}f = sd.get("wmtsstore.xml");if (Resources.exists(f)) {return new SingleResourceContents(f, f.getContents());}if (!isConfigDirectory(sd)) {LOGGER.warning("Ignoring store directory '" + sd.name() + "'");}// nothing foundreturn null;};for (Resource wsd : workspaceList) {// load the stores for this workspacetry (AsynchResourceIterator<SingleResourceContents> it =new AsynchResourceIterator<>(wsd, Resources.DirectoryFilter.INSTANCE, storeMapper)) {while (it.hasNext()) {SingleResourceContents SingleResourceContents = it.next();final String resourceName = SingleResourceContents.resource.name();if ("datastore.xml".equals(resourceName)) {loadDataStore(SingleResourceContents, catalog, xp, checkStores);} else if ("coveragestore.xml".equals(resourceName)) {loadCoverageStore(SingleResourceContents, catalog, xp);} else if ("wmsstore.xml".equals(resourceName)) {loadWmsStore(SingleResourceContents, catalog, xp);} else if ("wmtsstore.xml".equals(resourceName)) {loadWmtsStore(SingleResourceContents, catalog, xp);} else if (!isConfigDirectory(SingleResourceContents.resource)) {LOGGER.warning("Ignoring store directory '"+ SingleResourceContents.resource.name()+ "'");continue;}}}// load the layer groups for this workspaceResource layergroups = wsd.get("layergroups");if (layergroups != null) {loadLayerGroups(layergroups, catalog, xp);}}} else {LOGGER.warning("No 'workspaces' directory found, unable to load any stores.");}// layergroupsResource layergroups = resourceLoader.get("layergroups");if (layergroups != null) {loadLayerGroups(layergroups, catalog, xp);}xp.setUnwrapNulls(true);catalog.resolve();// re-enable extended validationif (!checkStores) {catalog.setExtendedValidation(true);}return catalog;}

这就是解析工作空间的核心代码了,并且可以看到它和上一步的关联的地方

catalog.add(ws);

扩展的说一下解析工作空间时用到了XStream技术,这个技术主要用于xml文件和java对象的互相转换

也就是说到此为止,Catalog的业务逻辑算是基本上梳理完了

五、总结

回顾上面的一坨坨代码,总结出来资源目录的处理就两条线

1.资源读取(上述4.2)【本质上就是读取文件】

2.资源的查询过滤(上述4.1)【本质上就是数组或者集合的增删改查】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/867914.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中国石油大学(华东)24计算机考研数据速览,计科学硕复试线288分!

中国石油大学&#xff08;华东&#xff09;计算机与通信工程学院是中国石油大学(华东)十三个教学院部之一&#xff0c;其前身是创建于1984年的计算机科学系&#xff0c;2001年撤系建院。伴随着学校50多年的风雨历程&#xff0c;计算机与通信工程学院也已经有了20多年的发展历史…

5.pwn Linux的延迟绑定机制

动态链接库 我们程序开发过程中都会用到系统函数&#xff0c;比如read&#xff0c;write&#xff0c;open等等 这些系统函数不需要我们实现&#xff0c;因为系统已经帮你完成这些工作&#xff0c;只需要调用即可&#xff0c;存放这些函数的库文件就是动态链接库。 通常情况下&…

[激光原理与应用-100]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 4 - 3C电池行业应用 - 不同的电池类型、焊接方式类型

目录 前言&#xff1a; 一、激光在3C行业的应用概述 1.1 概述 1.2 激光焊接在3C-电池行业的应用 1.3 动力电池的激光焊接工艺 1.3.1 概述 1.3.2 动力电池常见的焊接应用 1.电池壳体与盖板焊接 2.电池防爆阀密封 焊接 二、不同的电池的外形 2.1 软包锂电池 2.1.1 概述…

《机器学习》读书笔记:总结“第4章 决策树”中的概念

&#x1f4a0;决策树 基于树结构进行决策。 一棵决策树包括&#xff1a; 一个 根节点&#xff08;起点&#xff09;若干 叶节点&#xff08;没有下游节点的节点&#xff09;若干 内部节点(分支节点) 即&#xff1a; #mermaid-svg-Mxe3d0kNg29PM2n8 {font-family:"treb…

Open3D 删除点云中重叠的点(方法二)

目录 一、概述 1.1原理 1.2应用 二、代码实现 三、实现效果 3.1原始点云 3.2处理后点云 3.3数据对比 一、概述 在点云处理中&#xff0c;重叠点&#xff08;即重复点&#xff09;可能会对数据分析和处理的结果产生负面影响。因此&#xff0c;删除重叠点是点云预处理中常…

NextJs - SSR渲染解决antd首屏加载CSS样式的闪烁问题

NextJs - SSR渲染解决antd首屏加载CSS样式的闪烁问题 闪烁现状解决方案 闪烁现状 我们写一个非常简单的页面&#xff1a; import { Button } from antdexport default async function Page() {return <><Button typeprimary>AAA</Button></> }NextJs…

《昇思25天学习打卡营第14天|onereal》

第14天学习内容如下&#xff1a; Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成功运行。如您下载本文档为Python文件&#xff0c…

张量分解(1)——初探张量

&#x1f345; 写在前面 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;这里是hyk写算法了吗&#xff0c;一枚致力于学习算法和人工智能领域的小菜鸟。 &#x1f50e;个人主页&#xff1a;主页链接&#xff08;欢迎各位大佬光临指导&#xff09; ⭐️近…

SpEL表达式相关知识点

SpEL表达式 知识点 Spel概述 Spring 表达式&#xff0c;即 Spring Expression Language&#xff0c;简称 SpEL。 那么是什么SpEL表达式呢&#xff1f; SpEL (Spring Expression Language) 是一种在Spring框架中用于处理表达式的语言。SpEL中的表达式可以支持调用bean的方法…

IntelliJ IDEA菜单不见了设置找回方法

通过CtrAltS键按出设置 找到View,然后自定义一个快捷键,然后保存 使用自定义快捷键弹出改界面,点击Main Menu即可

传感器标定(一)摄像头内参标定

一、使用ROS进行手动标定安装 1、安装 image-view &usb_cam ⽤于驱动相机 sudo apt-get install ros-melodic-image-view sudo apt-get install ros-melodic-usb-cam2、查看系统视频设备 v4l2- ctl -d /dev/video0 --all 查询所有相机具体的参数包括width和height ls /…

内容监管与自由表达:Facebook的平衡之道

在当今数字化信息社会中&#xff0c;社交媒体平台不仅是人们交流和获取信息的主要渠道&#xff0c;也是自由表达的重要舞台。Facebook&#xff0c;作为全球最大的社交网络平台&#xff0c;连接了数十亿用户&#xff0c;形成了一个丰富多样的信息生态。然而&#xff0c;如何在维…

vue项目中 i18n(vue-i18n) 国际化解决方案,从安装到使用

1、国际化介绍 国际化&#xff08;Internationalization&#xff0c;通常缩写为"i18n"&#xff09;是指设计和开发软件应用程序&#xff0c;使其能够轻松地适应不同的语言、文化和地区的需求。国际化不仅仅涉及将文字翻译成其他语言&#xff0c;还包括调整日期、时间…

数据列表组件-报表

当数据在后端接口查询到&#xff0c;需要在页面展示出来&#xff0c;如果项目有很多report &#xff0c;可以把列表做一个组件 效果如下&#xff1a; js代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8" /><title&g…

中英双语介绍东京的商业核心区域:日本桥(Nihonbashi)

中文版 日本的日本桥&#xff08;Nihonbashi&#xff09; 位置 日本桥位于东京中央区&#xff0c;是东京市中心的重要商业和金融区之一。日本桥的名字来源于这里的同名桥梁“日本桥”&#xff0c;该桥建于江户时代&#xff0c;横跨日本桥川&#xff0c;是当时五街道的起点&a…

作业训练二编程题3. 数的距离差

【问题描述】 给定一组正整数&#xff0c;其中最大值和最小值分别为Max和Min, 其中一个数x到Max和Min的距离差定义为&#xff1a; abs(abs(x-Max)-(x-Min)) 其中abs()为求一个数的绝对值 【输入形式】 包括两行&#xff0c;第一行一个数n&#xff0c;表示第二行有n个正整数…

Linux内核链表使用方法

简介&#xff1a; 链表是linux内核中最简单&#xff0c;同时也是应用最广泛的数据结构。内核中定义的是双向链表。 linux的链表不是将用户数据保存在链表节点中&#xff0c;而是将链表节点保存在用户数据中。linux的链表节点只有2个指针(pre和next)&#xff0c;这样的话&#x…

AcWing 1260:二叉树输出

【题目来源】https://www.acwing.com/problem/content/1262/【题目描述】 树的凹入表示法主要用于树的屏幕或打印输出&#xff0c;其表示的基本思想是兄弟间等长&#xff0c;一个结点的长度要不小于其子结点的长度。 二叉树也可以这样表示&#xff0c;假设叶结点的长度为 1&…

SAP_MM模块-特殊业务场景下的系统实现方案

一、业务背景 目前公司有一种电商业务&#xff0c;卖的是备品配件&#xff0c;是公司先跟供应商采购&#xff0c;然后再销售给客户&#xff0c;系统账就是按照正常业务来流转&#xff0c;公司进行采购订单入库&#xff0c;然后销售订单出库。 不过这种备品配件&#xff0c;实…

STM32-I2C

本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. I2C通信1.1 I2C通信简介1.2 硬件电路1.3 I2C时序基本单元1.3.1 起始条件和终止条件1.3.2 发送一个字节1.3.3 接收一个字节1.3.4 发送应答和接收应答 1.4 I2C时序1.4.1 指定地址写1.4.2 当前地址读1.4.3 指定地址读…