Openlayers:海量图形渲染之图片渲染
最近由于在工作中涉及到了海量图形渲染的问题,因此我开始研究相关的解决方案。在这个过程中我阅读了文章 《Openlayers海量矢量面渲染优化》了解到了利用Canvas在前端生成图片渲染的思路,后来我又从同事那里了解到了后端生成图片渲染的思路。我认为这两种思路其实是相似的,在原理上都是一样,因此在这篇文章中我会把它们放在一起讨论。
一、什么是图片渲染?
在我了解了很多的海量图形渲染技术方案后,我发现其实有两个大的方向,我称之为“量变”与“质变”。其中“质变”就是指采用新的图形渲染逻辑来代替常规的图形渲染逻辑(常规的渲染逻辑就是将图形添加到矢量图层中渲染),而“图片渲染”就是一种典型的“质变”的方法。
“图片渲染”的基本思路思路是,我不直接在地图上绘制图形,而是先在其它的地方将图形绘制好,然后生成一张图片,最后再将图片以图片图层的形式渲染到地图上。
根据生成图片的位置方法的不同,“图片渲染”又可以分为前端图片生成和后端图片生成两种技术路线。
二、在前端生成图片实现图片渲染
1.利用Canvas生成图片并加载到地图上
在前端想要将空间图形绘制成一张图片,首推肯定是用Canvas来实现。
我们可以使用Openlayers的ImageCanvas
数据源,它可以支持将一个Canvas元素的图片作为数据源。
它的canvasFunction
属性接收一个Canvas函数,在Canvas函数中。我们需要创建一个Canvas元素,然后将图形绘制到Canvas画布上,并将Canvas元素作为函数的返回值,返回的Canvas元素将会作为一张图片。
最后我们再将ImageCanvas
数据源添加到一个ImageLayer
图层中,这样就可以在地图上加载图片了。
具体的实现方式可参考下面的代码:
// canvas渲染
function addRiver_canvas() {// 读取GeoJSON数据riverMeshFeatures = new GeoJSON().readFeatures(BJGrid, {dataProjection: "EPSG:4547",featureProjection: "EPSG:4326",});addColor();// 添加遮罩图层addMaskLayer();// 获取地图的像素尺寸const mapSize = window.map.getSize();// 使用canvas渲染riverCanvasLayer = new ImageLayer({properties: {name: "riverLayer_canvas",},zIndex: 1,source: new ImageCanvas({canvasFunction: (_extent,_resolution,_pixelRatio,size,_projection) => {// 创建画布const canvas = document.createElement("canvas");const ctx = canvas.getContext("2d");// 设置画布大小canvas.width = size[0];canvas.height = size[1];// 计算缩放比例const ratio = mapSize[0] / size[0];// 设置字体ctx.font = "60px Arial";const features = riverMeshFeatures;// 遍历要素,绘制features.forEach(feature => {// 每个格子设置颜色const color = feature.get("color") || "#00008B";// 几何要素let coordinates = feature.getGeometry().getCoordinates()[0][0];// 开始绘制路径ctx.beginPath();// 遍历环中的每个点并绘制格子let pixel = [0, 0];coordinates.forEach((point, index) => {// 未转比例的坐标const unconvertPixel = window.map.getPixelFromCoordinate(point);// 转比例坐标pixel = [unconvertPixel[0] / ratio, unconvertPixel[1] / ratio];if (index === 0) {// 将绘制起点移动到第一个点ctx.moveTo(pixel[0], pixel[1]);} else if (index === coordinates.length - 1) {// 绘制闭合路径ctx.closePath();} else {// 绘制线段到下一个点ctx.lineTo(pixel[0], pixel[1]);}});ctx.fillStyle = color;ctx.fill();// ctx.strokeStyle = "black";// ctx.lineWidth = 1;// ctx.stroke();});return canvas;},projection: "EPSG:4326",ratio: 1,interpolate: true,}),});window.map.addLayer(riverCanvasLayer);
}
2.如何进行图片更新?
如果加载到地图上的Canvas图片的内容需要更新,可以通过调用CanvasImage
数据源的changed()
方法重新绘制canvas图片。
3.如何实现图形的点击交互?
由于我们渲染在地图上的是一张图片,因此图片中的图形实际上是无法进行交互的,比如说我想高亮显示被点击的图形,或者点击图形后通过弹窗展示图形的属性信息,这些功能就难以实现。想要实现图形交互就需要另辟蹊径,这里我提供两个解决方案。
方法一 利用Geometry.intersectsCoordinate()
方法进行拓扑查询
大致的步骤如下:
- 首先通过地图的点击事件拿到点击位置的坐标
- 到图形数据池中去查找这个坐标与哪个图形相交,相交的图形就是被点击到的图形
- 最后去实现一些交互的效果
其中最关键的是第二步,我们要借助ol.geom.Geometry
的intersectsCoordinate()
方法来判断位置与图形是否相交。
// 添加网格的点击交互事件window.map.on("click", handleCellClick);
// 点击交互事件处理函数
function handleCellClick(event) {const coordinate = event.coordinate;let clickedFeature;const { length } = riverMeshFeatures;let index = 0;while (!clickedFeature && index < length) {const feature = riverMeshFeatures[index];const geometry = feature.getGeometry();if (geometry.intersectsCoordinate(coordinate)) {clickedFeature = feature;}index++;}console.log("点击事件", "网格" + index, clickedFeature);
}
方法二 使用"影子图层" 进行交互
大致的步骤如下:
- 创建一个矢量图层,并将所有图形都添加到图层中,同时禁止图层渲染,这个矢量图层就是我们图片图层的“影子图层”。
- 首先通过地图的点击事件拿到点击位置的坐标
- 在影子图层的数据源中查询这个坐标与哪个图形相交,相交的图形就是被点击到的图形
- 最后去实现一些交互的效果
// 创建矢量图层const riverShadowLayer = getVectorLayer({map: window.map,layerName: "riverLayer_shadow",style: new Style({fill: new Fill({color: "red",}),stroke: new Stroke({color: "black",width: 1,}),}),zIndex: 2,});riverShadowLayer.getSource().addFeatures(riverMeshFeatures);// 禁止矢量图层渲染riverShadowLayer.setVisible(false);window.map.on("click", event => {const pixel = event.pixel;const extent = new Point(window.map.getCoordinateFromPixel(pixel)).getExtent();const features = [];riverShadowLayer.getSource().forEachFeatureIntersectingExtent(extent, feature => {features.push(feature);});if (features.length > 0) {const feature = features[0];const properties = feature.getProperties();}});
三、在后端生成图片实现图片渲染
后端生成图片的这一整套流程我只是做了一定的了解,还没有进行实验测试,所以这里只阐述一个基本的思路,提供一些参考的代码,无法保证代码的准确性。
1.后端如何生成图片?
据我了解Java是有一个叫做GeoTools的库可以进行GIS开发。大致的思路是后端读取图形的数据,绘制成一个图层,然后将图层转为图片,最后将图片提供给前端。
当然具体的实现细节由于我不懂后端开发所以也没有办法给出介绍,我只能提供一个实例代码,有需要的可以参考一下。
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Files;
import net.coobird.thumbnailator.Thumbnails;
import org.geotools.data.Base64;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.label.LabelCacheImpl;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.*;
import org.geotools.geojson.feature.FeatureJSON;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.opengis.feature.simple.SimpleFeature;import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.Charset;
import java.util.List;
import java.util.*;public class GeojsonToImageUtils {/*** 获取GeoJSON图片* @param json* @param imagePath* @return* @throws IOException*/public static String getGeojsonBase64(String json,String imagePath) throws IOException {String imgStr=null;try {//String s = removeGeoJsonProperties(json);// String s1 = geojson2img(json, imagePath);// imgStr = getImgStr(s1);}catch (Exception e){e.printStackTrace();}return "data:image/png;base64,"+imgStr;}/*public void getJsonFilter(JSONObject jsonObject){JSONArray array = new JSONArray();jsonObject.getJSONArray("");}*//*** 生成图片* @param json* @param imgPath* @throws IOException*/public static String geojson2img(String json,String imgPath,String fileName) throws IOException {String caselsh = fileName.substring(0,fileName.lastIndexOf("."));JSONObject j1=JSONObject.parseObject(json);JSONArray array1=j1.getJSONArray("features");/*Set<Integer> s=new HashSet<>();for(int i=0;i<array1.size();i++){JSONObject obj=array1.getJSONObject(i);System.out.println(obj.getJSONObject("properties").getInteger("step"));s.add(obj.getJSONObject("properties").getInteger("step"));}System.out.println(s.size());*/String filename = caselsh+".png";FeatureJSON featureJSON = new FeatureJSON();FeatureCollection features = featureJSON.readFeatureCollection(json);MapContent mapContent = new MapContent();mapContent.setTitle("Quickstart");Style style2 = SysUtil.createDefaultStyle(); //.createDefaultStyleChl(); 产汇流土地利用用的样式Layer layer = new FeatureLayer(features, style2);mapContent.addLayer(layer);//mapContent.addLayers(getStyle1(features,json));File outputFile = new File(imgPath+filename);ImageOutputStream outputImageFile = null;FileOutputStream fileOutputStream = null;try {fileOutputStream = new FileOutputStream(outputFile);outputImageFile = ImageIO.createImageOutputStream(fileOutputStream);//这个宽高需要按照经纬度计算宽高//int w = 1500;//int h =1923;// 计算边界ReferencedEnvelope bounds = features.getBounds();double minX = bounds.getMinX();double minY = bounds.getMinY();double maxX = bounds.getMaxX();double maxY = bounds.getMaxY();// 设置图片的分辨率(每像素代表的经纬度单位)double resolution = 0.001;// 计算图片的宽高int w = (int) Math.ceil((maxX - minX) / resolution);int h = (int) Math.ceil((maxY - minY) / resolution);//2400 * 2;//int h = (int) (w * (768 / 506));//(w * (768 / 506));BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = bufferedImage.createGraphics();mapContent.getViewport().setMatchingAspectRatio(true);mapContent.getViewport().setScreenArea(new Rectangle(Math.round(w), Math.round(h)));mapContent.getViewport().setBounds(bounds);g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);Rectangle outputArea = new Rectangle(w, h);//Rectangle outputArea = new Rectangle(250, 250);//outputArea.setLocation(70,100);GTRenderer renderer = new StreamingRenderer();LabelCacheImpl labelCache = new LabelCacheImpl();Map<Object, Object> hints = renderer.getRendererHints();if (hints == null) {hints = new HashMap<Object, Object>();}hints.put(StreamingRenderer.LABEL_CACHE_KEY, labelCache);renderer.setRendererHints(hints);renderer.setMapContent(mapContent);renderer.paint(g2d, outputArea, bounds);ImageIO.write(bufferedImage, "png", outputImageFile);//下面这个是用来处理压缩输出图片的//File yasuoTargetFile=new File("C:\\Users\\GuLiXin\\Desktop\\新建文件夹\\压缩后\\lucc\\"+filename);//压缩比例// Thumbnails.of(outputFile).scale(1f).toFile(yasuoTargetFile);System.out.println(filename+"转换完成");if(outputFile.exists()){outputFile.delete();} //yasuo(imgPath+filename,imgPath+UUID.randomUUID().toString()+".png");} catch (IOException ex) {ex.printStackTrace();} finally {try {if (outputImageFile != null) {outputImageFile.flush();outputImageFile.close();fileOutputStream.flush();fileOutputStream.close();}} catch (IOException e) {// don't care now}}return imgPath+filename;}/*** 生成样式* @param features* @param json* @return*/public static List<Layer> getStyle1(FeatureCollection features, String json) throws IOException {Integer geojsonType = getGeojsonType(json);if (geojsonType == 1){Style point = SLD.createSimpleStyle(features.getSchema(),roundColor());return null;}else if (geojsonType ==2){Style lineStyle = SLD.createLineStyle(roundColor(), 1);return null;}else if(geojsonType == 3){List<Layer> result = new ArrayList<Layer>();JSONObject jsonObject = JSONObject.parseObject(json);JSONArray feature = jsonObject.getJSONArray("features");JSONArray array1 = new JSONArray();JSONArray array2 = new JSONArray();for (int i = 0; i < feature.size(); i++) {JSONObject resultJson = JSONObject.parseObject(feature.get(i).toString());JSONObject properties = resultJson.getJSONObject("properties");Integer id = properties.getInteger("id");if (id > 7000){array1.add(feature.get(i));}else {array2.add(feature.get(i));}}JSONObject j = new JSONObject();//(JSONObject)jsonObject.clone();j.put("features",array1);JSONObject k = new JSONObject();//(JSONObject)jsonObject.clone();k.put("features",array2);FeatureJSON featureJSON = new FeatureJSON();FeatureCollection featureCollection = featureJSON.readFeatureCollection(j.toString());FeatureCollection featureCollection2 = featureJSON.readFeatureCollection(k.toString());Layer layer = new FeatureLayer(featureCollection, SLD.createPolygonStyle(Color.white, roundColor(), 1));Layer layer1 = new FeatureLayer(featureCollection2, SLD.createPolygonStyle(Color.RED, roundColor(), 1));result.add(layer);result.add(layer1);return result;}return null;}/*** 生成样式* @param features* @param json* @return*/public static Style getStyle(FeatureCollection features, String json){Integer geojsonType = getGeojsonType(json);if (geojsonType == 1){Style point = SLD.createSimpleStyle(features.getSchema(),roundColor());return point;}else if (geojsonType ==2){Style lineStyle = SLD.createLineStyle(roundColor(), 1);return lineStyle;}else if(geojsonType == 3){Style polygonStyle = SLD.createPolygonStyle(Color.ORANGE, roundColor(), 1);return polygonStyle;}return null;}/*** 随机颜色* @return*/public static Color roundColor(){Random random = new Random();float hue = random.nextFloat();float saturation = (random.nextInt(2000) + 1000) / 10000f;float luminance = 0.9f;Color color = Color.getHSBColor(hue, saturation, luminance);return color;}/*** 去除properties属性* @param jsonstr* @return*/public static String removeGeoJsonProperties(String jsonstr){JSONObject json = (JSONObject) JSONObject.parse(jsonstr);JSONArray features = (JSONArray) json.get("features");for (int i = 0; i < features.size(); i++) {JSONObject feature = features.getJSONObject(i);feature.remove("properties");}return json.toJSONString();}/*** 获取GeoJSON类型* @param strJson* @return*/public static Integer getGeojsonType(String strJson){JSONObject json = (JSONObject) JSONObject.parse(strJson);JSONArray features = (JSONArray) json.get("features");JSONObject feature0 = features.getJSONObject(0);String strType = ((JSONObject)feature0.get("geometry")).getString("type").toString();Integer geoType = null;if ("Point".equals(strType)) {geoType = 1;} else if ("MultiPoint".equals(strType)) {geoType = 1;} else if ("LineString".equals(strType)) {geoType = 2;} else if ("MultiLineString".equals(strType)) {geoType = 2;} else if ("Polygon".equals(strType)) {geoType = 3;} else if ("MultiPolygon".equals(strType)) {geoType = 3;}return geoType;}/*** 将图片转换成Base64编码* @param imgFile 待处理图片* @return*/public static String getImgStr(String imgFile) {// 将图片文件转化为字节数组字符串,并对其进行Base64编码处理InputStream in = null;byte[] data = null;// 读取图片字节数组try {in = new FileInputStream(imgFile);data = new byte[in.available()];in.read(data);in.close();} catch (IOException e) {e.printStackTrace();}return Base64.encodeBytes(data);//Base64.encodeBase64String(data);}/*** 判断GeoJSON格式是否正确* @param strJson* @return*/public static boolean checkGeojson(String strJson){Boolean flag = true;JSONObject json = (JSONObject) JSONObject.parse(strJson);if(!json.containsKey("features")){return false;}JSONArray features = (JSONArray) json.get("features");for (int i = 0; i < features.size(); i++) {JSONObject feature = features.getJSONObject(i);if (!feature.containsKey("geometry")){flag = false;break;}JSONObject geometry = (JSONObject)feature.get("geometry");if (!geometry.containsKey("type")){flag = false;break;}if (!geometry.containsKey("coordinates")){flag = false;break;}}return flag;}/*** 生成多边形* @param json* @param imgPath* @throws IOException*/public static String geojson3img(String json, String imgPath, String step, List<Layer> layerList, ReferencedEnvelope bounds) throws IOException {String filename = UUID.randomUUID().toString()+"-"+step+".png";//FeatureJSON featureJSON = new FeatureJSON();//FeatureCollection features = featureJSON.readFeatureCollection(json);MapContent mapContent = new MapContent();mapContent.setTitle("Quickstart");mapContent.addLayers(layerList);File outputFile = new File(imgPath+filename);ImageOutputStream outputImageFile = null;FileOutputStream fileOutputStream = null;try {fileOutputStream = new FileOutputStream(outputFile);outputImageFile = ImageIO.createImageOutputStream(fileOutputStream);//int w = 384;int w = 9960;//ReferencedEnvelope bounds = features.getBounds();int h = 4800;//int h = (int) (w * (768 / 506));//(w * (768 / 506));BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = bufferedImage.createGraphics();mapContent.getViewport().setMatchingAspectRatio(true);mapContent.getViewport().setScreenArea(new Rectangle(Math.round(w), Math.round(h)));mapContent.getViewport().setBounds(bounds);g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);Rectangle outputArea = new Rectangle(9960, 4800);//Rectangle outputArea = new Rectangle(250, 250);//outputArea.setLocation(70,100);GTRenderer renderer = new StreamingRenderer();LabelCacheImpl labelCache = new LabelCacheImpl();Map<Object, Object> hints = renderer.getRendererHints();if (hints == null) {hints = new HashMap<Object, Object>();}hints.put(StreamingRenderer.LABEL_CACHE_KEY, labelCache);renderer.setRendererHints(hints);renderer.setMapContent(mapContent);renderer.paint(g2d, outputArea, bounds);ImageIO.write(bufferedImage, "png", outputImageFile);} catch (IOException ex) {ex.printStackTrace();} finally {try {if (outputImageFile != null) {outputImageFile.flush();outputImageFile.close();fileOutputStream.flush();fileOutputStream.close();mapContent.dispose();}} catch (IOException e) {// don't care now}}return imgPath+filename;}/*** 获取指定文件夹下所有文件,不含文件夹里的文件** @param dirFile 文件夹* @return*/public static List<File> getAllFile(File dirFile) {// 如果文件夹不存在或着不是文件夹,则返回 nullif (Objects.isNull(dirFile) || !dirFile.exists() || dirFile.isFile())return null;File[] childrenFiles = dirFile.listFiles();if (Objects.isNull(childrenFiles) || childrenFiles.length == 0)return null;List<File> files = new ArrayList<>();for (File childFile : childrenFiles) {// 如果是文件,直接添加到结果集合if (childFile.isFile()) {files.add(childFile);}}return files;}public static String shape2Geojson(String shpPath) {StringBuffer sb = new StringBuffer();sb.append("{\"type\":\"FeatureCollection\", \"features\": ");FeatureJSON fJson = new FeatureJSON();File file = new File(shpPath);JSONArray array = new JSONArray();JSONObject json = new JSONObject();try {ShapefileDataStore store = new ShapefileDataStore(file.toURI().toURL());//设置编码((ShapefileDataStore) store).setCharset(Charset.forName("ISO-8859-1"));//store.setCharset(Charset.forName("UTF-8"));//文件名称String typeName = store.getTypeNames()[0];SimpleFeatureSource featureSource = store.getFeatureSource(typeName);SimpleFeatureIterator iterator = featureSource.getFeatures().features();while (iterator.hasNext()) {SimpleFeature feature = iterator.next();StringWriter writer = new StringWriter();fJson.writeFeature(feature, writer);json = JSONObject.parseObject(writer.toString());//使用jsonArray可以把所有数据转成一条;不使用,array.add(json);}iterator.close();sb.append(array.toJSONString());sb.append("}");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}WriteStringToFile(sb.toString(),"C:\\Users\\GuLiXin\\Desktop\\bhp\\lucc.json");return sb.toString();}public static void WriteStringToFile(String string,String jsonPath) {String filePath = jsonPath;try {File file = new File(filePath);PrintStream ps = new PrintStream(new FileOutputStream(file));ps.append(string);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void main(String[] args) throws Exception {// String geojson = JsonFileUtil.readJsonFile("E:\\data-2\\step1.json");//查询//存入map筛选//String geojsonBase64 = geojson2img(geojson, "C:\\Users\\GuLiXin\\Desktop\\新建文件夹\\");File f=new File("E:\\data-50");List<File> fileList=getAllFile(f);;for (File file:fileList){String geojson = JsonFileUtil.readJsonFile(file.getPath());geojson2img(geojson, "C:\\Users\\GuLiXin\\Desktop\\新建文件夹\\",file.getName());}// File f=new File("C:\\Users\\GuLiXin\\Desktop\\bhp\\土地利用_2005.shp");// FileDataStore dataStore=FileDataStoreFinder.getDataStore(f);// String geoJson=shape2Geojson("C:\\Users\\GuLiXin\\Desktop\\bhp\\土地利用_2005.shp");//System.out.println(geoJson);//File file=new File("C:\\Users\\GuLiXin\\Desktop\\data\\lucc.json");//String geojson = JsonFileUtil.readJsonFile(file.getPath());// geojson2img(geoJson, "C:\\Users\\GuLiXin\\Desktop\\bhp",f.getName());/*String shpPath = "";//shp文件地址String geojsonPath = "";//生成的GeoJSON文件地址String iamgepath="";String geojson = ParsingShpFileUtils.shape2Geojson(shpPath, geojsonPath);String geojsonBase64 = getGeojsonBase64(geojson, iamgepath);//base64 一般存库里*/}}
2.前端如何加载图片?
前端在拿到后端生成的图片后,通过ImageLaeyr
图层 + ImageStatic
数据源 就可以实现加载。
使用方式可以参考这个Openlayers示例:Image Reprojection
参考资料
- Openlayers海量矢量面渲染优化_openlayers渲染海量数据-CSDN博客
- OpenLayers v10.5.0 API - Class: ImageCanvasSource
- Image Reprojection
相关文章:
Openlayers:海量图形渲染之图片渲染
最近由于在工作中涉及到了海量图形渲染的问题,因此我开始研究相关的解决方案。在这个过程中我阅读了文章 《Openlayers海量矢量面渲染优化》了解到了利用Canvas在前端生成图片渲染的思路,后来我又从同事那里了解到了后端生成图片渲染的思路。我认为这两种…...
自定义组件触发饿了么表单校验
饿了么的表单控件,如果存在自定义组件更改了值,例如在el-from中存在原生input组件很有可能没法触发表单校验,下拉框或者弹框组件仍然是报红边框。 这是因为饿了么的输入框或者下拉框更改值的时候会自动触发表单校验,但是封装过后的…...
【C++】从零实现Json-Rpc框架(1)
目录 一、项目介绍 二、技术选型 1. RPC的实现方案: 2. 网络传输的参数和返回值怎么映射到对应的RPC 接口上? 3. 网络传输怎么做? 4. 序列化和反序列化? 三、开发环境 四、环境搭建 Ubuntu-22.04 环境搭建 项目汇总&…...
[实战] linux驱动框架与驱动开发实战
linux驱动框架与驱动开发实战 Linux驱动框架与驱动开发实战一、Linux驱动框架概述1.1 Linux驱动的分类1.2 Linux驱动的基本框架 二、Linux驱动关键API详解2.1 模块相关API2.2 字符设备驱动API2.3 内存管理API2.4 中断处理API2.5 PCI设备驱动API 三、Xilinx XDMA驱动开发详解3.1…...
【详细】MySQL 8 安装解压即用 (包含MySQL 5 卸载)
卸载MySQL 1.卸载 2.安装目录删除残余文件(当初安装的位置) 3.删除programData下面的mysql数据文件 4.检查mysql服务是否存在,如果存在则删除(先暂停mysql服务) sc delete mysql 5.删除注册表中残留信息 安装MySQL 8&…...
Linux:(五种IO模型)
目录 一、对IO的重新认识 二、IO的五种模型 1.阻塞IO 2.非阻塞IO 3.信号驱动IO 4.IO多路转接 5.异步IO 6.一些概念的解释 三、非阻塞IO的代码实现 1.fcntl 2.实现主程序 一、对IO的重新认识 如果有人问你IO是什么,你该怎么回答呢? 你可能会说…...
初识数据结构——Java包装类与泛型:从入门到源码解析
【深入浅出】Java包装类与泛型:从入门到源码解析 🌟 一、开篇一问:为什么我们需要包装类? Java作为一门"面向对象"的语言,却保留了8个"非对象"的基本数据类型(有传言说,是…...
【计算机网络】Linux配置SNAT策略
什么是NAT? NAT 全称是 Network Address Translation(网络地址转换),是一个用来在多个设备共享一个公网 IP上网的技术。 NAT 的核心作用:将一个网络中的私有 IP 地址,转换为公网 IP 地址,从而…...
与 AI 共舞:解锁自我提升的无限可能
与 AI 共舞:解锁自我提升的无限可能 在数字化浪潮的汹涌冲击下,人工智能(AI)正以前所未有的速度重塑着世界的每一个角落。从日常生活的点滴便利到复杂工作的高效推进,AI 的力量无处不在。然而,面对 AI 的强…...
Android学习总结之算法篇五(字符串)
字符串求回文字串数目 public class CountPalindromicSubstrings {/*** 此方法用于计算字符串中回文子串的数量* param s 输入的字符串* return 回文子串的数量*/public static int countSubstrings(String s) {// 若输入字符串为空或长度为 0,直接返回 0if (s nu…...
使用人车关系核验API快速核验车辆一致性
一、 引言 随着车辆交易的日益频繁,二手车市场和金融领域的汽车抵押业务蓬勃发展。然而,欺诈和盗窃行为也时有发生,给行业带来了不小的冲击。例如,3月20日央视曝光的“新能源车虚假租赁骗补”产业链,以及某共享汽车平…...
day 8 TIM定时器
一、STM32 定时器概述 1. 定时器的概述定时器的基本功能,但是 STM32 的定时器除了具有定时功能之外,也具有定时器中断功能,还具有输入捕获(检测外部信号)以及输出比较功能(输出不同的脉冲)&…...
硬币找零问题
硬币找零问题:假设需要找零的金额为C,最少要用多少面值为p1<p2<…<pn的硬币(面值种类为n,且假设每种面值的硬币都足够多)? 贪心算法的基本原理是:遵循某种既定原则,不断…...
【微机及接口技术】- 第四章 内部存储器及其接口(上)
文章目录 第一节一、存储器的分类二、存储器的层次结构 第二节 半导体存储器一、半导体存储器的基本结构二、半导体存储器的分类1. 只读存储器 ROM2. 随机存储器 RAM 三、内存的主要性能指标1. 存储容量2. 存取时间3. 存取周期4. 可靠性5. 性价比 四、典型的半导体存储器芯片 本…...
tomcat的web三大组件Sciidea搭建web/maven的tomcat项目
文章目录 1. web项目的搭建1. 创建web项目2.修改web.xml版本3.添加servlet、jsp依赖4.servlet示例(使用注解)5.配置tomcat6.添加artifact7.部署8.启动tomcat、访问9.打war包10.部署到tomcat 2.maven的项目搭建1.创建项目图解 2.tomcat启动方式图解idea打…...
《SQL赋能人工智能:解锁特征工程的隐秘力量》
在当今的科技发展进程中,人工智能(AI)已经成为推动各领域变革的核心驱动力。而在人工智能的庞大体系里,特征工程占据着举足轻重的地位,它是将原始数据转化为能够让模型有效学习的特征的关键环节。鲜有人深入探讨的是&a…...
SQLmap工具使用
1. sqlmap介绍 sqlmap是一款自动化的SQL注入工具,用于检测和利用web应用程序中的SQL注入漏洞。不需要我们进行手注,当我们输入url地址后,会自动进行注入指令并将payload返回显示。 在kali中自带。在本机中需要下载,在相应的路径…...
Kubernetes集群管理详解:从入门到精通
1. 引言 Kubernetes(简称k8s)作为当今最流行的容器编排平台,已成为云原生应用部署和管理的事实标准。本文将深入探讨k8s集群管理的各个方面,为运维工程师和开发人员提供一个全面的指南。 2. Kubernetes架构概览 在深入具体的管理任务之前,让我们先回顾一下Kubernetes的基本架…...
【含文档+PPT+源码】基于Python的全国景区数据分析以及可视化实现
项目介绍 本课程演示的是一款基于Python的全国景区数据分析以及可视化实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 带你从零开始部署运行本套系统 该…...
鸿蒙开发者高级认证编程题库
题目一:跨设备分布式数据同步 需求描述 开发一个分布式待办事项应用,要求: 手机与平板登录同一华为账号时,自动同步任务列表任一设备修改任务状态(完成/删除),另一设备实时更新任务数据在设备离线时能本地存储,联网后自动同步实现方案 // 1. 定义分布式数据模型 imp…...
【网络安全】安全的网络设计
网络设计是网络安全的基础,一个好的网络设计可以有效的防止攻击者的入侵。在本篇文章中,我们将详细介绍如何设计一个安全的网络,包括网络架构,网络设备,网络策略,以及如何处理网络安全事件。 一、网络架构…...
基于FLask的重庆市造价工程信息数据可视化分析系统
【FLask】基于FLask的重庆市造价工程信息数据可视化分析系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 在当今快速发展的建筑工程行业中,造价信息的准确性和时效性对于项目决…...
swift-08-属性、汇编分析inout本质
一、Swift中跟实例相关的属性可以分为2大类 1.1 存储属性( Stored Property) 类似于成员变量这个概念 存储在实例的内存中 结构体、类可以定义存储属性 枚举不可以定义存储属性(因为枚举只存储关联值和case) 1.2 计算属性&am…...
Java 大视界 -- Java 大数据在智能医疗远程护理与患者健康管理中的应用与前景(175)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
大数据技术发展与应用趋势分析
大数据技术发展与应用趋势分析 文章目录 大数据技术发展与应用趋势分析1. 大数据概述2 大数据技术架构2.1 数据采集层2.2 数据存储层2.3 数据处理层2.4 数据分析层 3 大数据发展趋势3.1 AI驱动的分析与自动化3.2 隐私保护分析技术3.3 混合云架构的普及3.4 数据网格架构3.5 量子…...
如何在Ubuntu上安装Dify
如何在Ubuntu上安装Dify 如何在Ubuntu上安装docker 使用apt安装 # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg…...
ffmpeg音频分析
对一个16k 单声道音频,生成频谱图 ./ffmpeg -i input.wav -lavfi "showspectrumpics800x400:modecombined:scalelin:gain1.5" spectrum.png...
每天五分钟深度学习框架pytorch:搭建LSTM完成手写字体识别任务?
本文重点 前面我们学习了LSTM的搭建,我们也学习过很多卷积神经网络关于手写字体的识别,本文我们使用LSTM来完成手写字体的识别。 网络模型的搭建 class RNN(nn.Module):def __init__(self,in_dim,hidden_dim,n_layer,n_class):super(RNN,self).__init__()self.n_layer=n_la…...
Maven工具学习使用(七)——Maven属性
内置属性 主要有两个常用的属性${basedir}表示项目的根目录,即包含pom.xml文件的目录;$[version]表示项目版本。 POM属性 使用该类属性引用POM文件中对应元素的值。例如${project.artifactId}就对应了元素的值,常用的POM属性包括: ${project.build.sourceDirectory} 项…...
【Linux网络与网络编程】05.应用层自定义协议序列化和反序列化
前言 本篇博客通过网络计算器的实现来帮助各位理解应用层自定义协议以及序列化和反序列化。 一、认识自定义协议&&序列化和反序列化 我们程序员写的一个个解决我们实际问题,满足我们日常需求的网络程序都是在应用层。前面我们说到:协议是一种…...
搭建K8S-1.23
0、简介 这里只用3台服务器来做一个简单的集群 地址主机名192.168.160.40kuber-master-1192.168.160.41kuber-master-2192.168.160.42kuber-node-1 1、关闭三个服务 (1)防火墙 systemctl stop firewalld (2)Selinux setenf…...
解决Spring Boot Test中的ByteBuddy类缺失问题
目录 解决Spring Boot Test中的ByteBuddy类缺失问题前奏问题描述问题解决第一步:移除ByteBuddy的特定版本号第二步:更新maven-surefire-plugin配置第三步:清理并重新构建项目 结语 解决Spring Boot Test中的ByteBuddy类缺失问题 前奏 今天&…...
npm 项目命名规则
以下是 npm 项目命名规则的详细说明: 一、核心命名规则 必须使用小写字母 名称中不能包含大写字母。原因: 跨平台兼容性(如 Linux 区分大小写,而 Windows 不区分)。避免命令行和 URL 中的大小写冲突(例如包…...
#SVA语法滴水穿石# (012)关于 first_match、throughout、within 的用法
我们今天学习, SystemVerilog 断言(SVA)中 first_match、throughout、within 运算符。 1. first_match 定义与作用 功能:在可能产生 多个匹配结果 的复合序列(如 or 或重复操作符)中,仅选择第…...
基于springboot+vue的二手车交易系统
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
洛谷P11999.投入严厉地本地
洛谷P11999.投入严厉地本地 题目 题目解析及思路 题目要求根据两个字符串s和t,反推出一个映射集合f,其中s的每一个长度为k的子串都可以映射成单个字符或空字符 算出最终映射集合有多少个空字符,用全排列函数去搜索所有情况,判断…...
低代码开发平台:飞帆中新增控件、修改他人控件
飞帆是一个自由的控件平台。所有的网页都由控件搭建而成。 在我的资源、我的控件中,点击新增可以新增控件 对于他人的控件,点击复制控件展开,点击复制到我的控件 飞帆中的控件是使用 Vue2 来实现的...
Openstack指南
什么是云计算 概念 云计算是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息,可以按需求提供给计算机和其他设备。用户不需要了解”云“中的基础设施细节,不必具有相应的专业知识,也无需直接控制。云…...
[自制调试工具]构建高效调试利器:Debugger 类详解
一、引言 在软件开发的漫漫征程中,调试就像是一位忠诚的伙伴,时刻陪伴着开发者解决代码里的各类问题。为了能更清晰地了解程序运行时变量的状态,我们常常需要输出各种变量的值。而 Debugger 类就像是一个贴心的调试助手,它能帮我…...
【网络IP】原生IP是什么?如何获取海外原生IP?
一、什么是原生IP 原生IP地址是互联网服务提供商(ISP)直接分配给用户的真实IP地址,无需代理或转发。这类IP的注册国家与IP所在服务器的注册地相符。这种IP地址直接与用户的设备或网络关联,不会被任何中间服务器或代理转发或隐藏。…...
【小沐学Web3D】three.js 加载三维模型(React Three Fiber)
文章目录 1、简介1.1 Three.js1.2 React Three Fiber 2、测试2.1 初始化环境2.2 app.js修改(显示内置立方体)2.3 app.js修改(显示内置球体)2.4 app.js修改(显示自定义立方体)2.5 app.js修改(显示…...
HTTP查询参数示例(XMLHttpRequest查询参数)(带查询参数的HTTP接口示例——以python flask接口为例)flask查询接口
文章目录 HTTP查询参数请求示例接口文档——获取城市列表代码示例效果 带查询参数的HTTP接口示例——以python flask接口为例app.pyREADME.md运行应用API示例客户端示例关键实现说明:运行方法: HTTP查询参数请求示例 接口文档——获取城市列表 代码示例…...
【玩泰山派】1、mac上使用串口连接泰山派
文章目录 前言picocom工具连接泰山派安装picocom工具安装ch340的驱动串口工具接线使用picocom连接泰山派 参考 前言 windows上面有xshell这个好用的工具可以使用串口连接板子,在mac上好像没找到太好的工具,只能使用命令行工具去搞了。 之前查找说mac上…...
【玩泰山派】0、mac utm安装windows10
文章目录 前言安装过程utm安装下载windows ios镜像安装windows系统解决共享文件夹拷贝受限问题 参考 前言 使用mac开发泰山派,但是买的泰山派没有emmc,只有sd卡,要使用瑞芯微的sd卡烧录工具,这个工具好像只支持windows࿰…...
html 给文本两端加虚线自适应
效果图: <div class"separator">文本 </div>.separator {width: 40%;border-style: dashed;display: flex;align-items: center;color: #e2e2e2;font-size: 14px;line-height: 20px;border-color: #e2e2e2;border-width: 0; }.separator::bef…...
Android学习总结之应用启动流程(从点击图标到界面显示)
一、用户交互触发:Launcher 到 AMS 的跨进程通信 1. Launcher 处理点击事件(应用层) 当用户点击手机桌面上的应用图标时,Launcher(桌面应用)首先捕获点击事件。每个图标对应一个启动 Intent(通…...
Vue2与Vue3不同
Vue3 设计思想与核心变化详解 一、Vue3 设计思想与 Vue2 差异对比 响应式系统重构Vue2 实现(基于 Object.defineProperty) // 在 Vue2 中,通过 data 选项返回一个对象,对象中的属性会被 Object.defineProperty 转换为响应式数据…...
《Java实战:素数检测算法优化全解析——从暴力枚举到筛法进阶》
文章目录 摘要一、需求分析二、基础实现代码与问题原始代码(暴力枚举法)问题分析 三、优化版代码与解析优化1:平方根范围剪枝优化2:偶数快速跳过完整优化代码 四、性能对比五、高阶优化:埃拉托斯特尼筛法算法思想代码实…...
解决报错:node:internal/errors:496 ErrorCaptureStackTrace(err);
报错信息 我使用npm init vuelatest创建项目时出现如下报错 node:internal/errors:496 ErrorCaptureStackTrace(err); ^ TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module “file:///D:/develop/nodejs/node_cache/_npx/2f7e7bff16d1c534/node_modules/create-vue/loc…...
【Linux笔记】进程管理章节笔记
一、进程与线程的概念 1、进程 1)概念 进程是程序的执行实例,拥有独立的资源(如内存、文件描述符等)。每个进程在创建时会被分配唯一的进程ID,即为PID,也叫进程编号。 2)特点 资源隔离&#…...