大数据存储项目-基于Flink的高速公路ETC入深圳数据实时分析平台

大数据项目总体流程分为以下4个方面:
数据采集,数据存储与管理,数据处理与分析,数据解释与可视化。

数据源

下载 高速公路ETC入深圳数据,数据量:178396条

https://opendata.sz.gov.cn/data/dataSet/toDataDetails/29200_00403621

项目要求

(1)每秒产生50+条数据,可以采用网络压力测试工具产生多点并发的高速数据流https://blog.csdn.net/moonpure/article/details/72674374,例如JMeter

(2)利用Kafka对高速数据进行缓存

(3)利用HBase或者MyCat+Mysql对数据进行存储。

(4)如果采用MyCat+Mysql方式存储数据,需要设计业务逻辑对数据进行分片,并对全局数据进行查询和统计

(5)如果采用HBase方式存储数据,需要设计业务逻辑对rowkey进行设计,并对数据进行“key-value”查询。

(6)对两种方式查询或者统计的结果进行可视化展示,要求每分钟一次对结果进行刷新。

项目流程

项目整体流程:
在这里插入图片描述

1.数据爬取与清洗

(之后补充,可以直接使用官网提供的一万条数据示例)

2.jmeter模拟高并发数据流

1)官网下载jmeter
2)启动jmeter
在jmeter安装目录下找到bin目录下的jmeter.bat双击启动jmeter
3) 模拟高并发数据流
创建一个多线程组
在这里插入图片描述
添加一个请求指令,指定每个线程每次的请求
在这里插入图片描述
classname更换为kafkameter的生产者请求
在这里插入图片描述
kafkaProducerSampler(将其写入一个meaven项目中):

package co.signal.kafkameter;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;

import com.google.common.base.Strings;

import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.log.Logger;

/**
 * A {@link org.apache.jmeter.samplers.Sampler Sampler} which produces Kafka messages.
 *
 *
 * @see "http://ilkinbalkanay.blogspot.com/2010/03/load-test-whatever-you-want-with-apache.html"
 * @see "http://newspaint.wordpress.com/2012/11/28/creating-a-java-sampler-for-jmeter/"
 * @see "http://jmeter.512774.n5.nabble.com/Custom-Sampler-Tutorial-td4490189.html"
 */
public class KafkaProducerSampler extends AbstractJavaSamplerClient {

  private static final Logger log = LoggingManager.getLoggerForClass();

  /**
   * Parameter for setting the Kafka brokers; for example, "kafka01:9092,kafka02:9092".
   */
  private static final String PARAMETER_KAFKA_BROKERS = "kafka_brokers";

  /**
   * Parameter for setting the Kafka topic name.
   */
  private static final String PARAMETER_KAFKA_TOPIC = "kafka_topic";

  /**
   * Parameter for setting the Kafka key.
   */
  private static final String PARAMETER_KAFKA_KEY = "kafka_key";

  /**
   * Parameter for setting the Kafka message.
   */
  private static final String PARAMETER_KAFKA_MESSAGE = "kafka_message";

  /**
   * Parameter for setting Kafka's {@code serializer.class} property.
   */
  private static final String PARAMETER_KAFKA_MESSAGE_SERIALIZER = "kafka_message_serializer";

  /**
   * Parameter for setting Kafka's {@code key.serializer.class} property.
   */
  private static final String PARAMETER_KAFKA_KEY_SERIALIZER = "kafka_key_serializer";

  /**
   * Parameter for setting the Kafka ssl keystore (include path information); for example, "server.keystore.jks".
   */
  private static final String PARAMETER_KAFKA_SSL_KEYSTORE = "kafka_ssl_keystore";

  /**
   * Parameter for setting the Kafka ssl keystore password.
   */
  private static final String PARAMETER_KAFKA_SSL_KEYSTORE_PASSWORD = "kafka_ssl_keystore_password";

  /**
   * Parameter for setting the Kafka ssl truststore (include path information); for example, "client.truststore.jks".
   */
  private static final String PARAMETER_KAFKA_SSL_TRUSTSTORE = "kafka_ssl_truststore";

  /**
   * Parameter for setting the Kafka ssl truststore password.
   */
  private static final String PARAMETER_KAFKA_SSL_TRUSTSTORE_PASSWORD = "kafka_ssl_truststore_password";

  /**
   * Parameter for setting the Kafka security protocol; "true" or "false".
   */
  private static final String PARAMETER_KAFKA_USE_SSL = "kafka_use_ssl";

  /**
   * Parameter for setting encryption. It is optional.
   */
  private static final String PARAMETER_KAFKA_COMPRESSION_TYPE = "kafka_compression_type";

  /**
   * Parameter for setting the partition. It is optional.
   */
  private static final String PARAMETER_KAFKA_PARTITION = "kafka_partition";

  //private Producer<Long, byte[]> producer;

  private KafkaProducer<String, String> producer;


  @Override
  public void setupTest(JavaSamplerContext context) {
    Properties props = new Properties();

    props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, context.getParameter(PARAMETER_KAFKA_BROKERS));
    props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, context.getParameter(PARAMETER_KAFKA_KEY_SERIALIZER));
    props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, context.getParameter(PARAMETER_KAFKA_MESSAGE_SERIALIZER));
    props.put(ProducerConfig.ACKS_CONFIG, "1");

    // check if kafka security protocol is SSL or PLAINTEXT (default)
    if(context.getParameter(PARAMETER_KAFKA_USE_SSL).equals("true")){
      log.info("Setting up SSL properties...");
      props.put("security.protocol", "SSL");
      props.put("ssl.keystore.location", context.getParameter(PARAMETER_KAFKA_SSL_KEYSTORE));
      props.put("ssl.keystore.password", context.getParameter(PARAMETER_KAFKA_SSL_KEYSTORE_PASSWORD));
      props.put("ssl.truststore.location", context.getParameter(PARAMETER_KAFKA_SSL_TRUSTSTORE));
      props.put("ssl.truststore.password", context.getParameter(PARAMETER_KAFKA_SSL_TRUSTSTORE_PASSWORD));
    }

    String compressionType = context.getParameter(PARAMETER_KAFKA_COMPRESSION_TYPE);
    if (!Strings.isNullOrEmpty(compressionType)) {
      props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, compressionType);
    }

    producer = new KafkaProducer<String, String>(props);
  }

  @Override
  public void teardownTest(JavaSamplerContext context) {
    producer.close();
  }

  @Override
  public Arguments getDefaultParameters() {
    Arguments defaultParameters = new Arguments();
    defaultParameters.addArgument(PARAMETER_KAFKA_BROKERS, "${PARAMETER_KAFKA_BROKERS}");
    defaultParameters.addArgument(PARAMETER_KAFKA_TOPIC, "${PARAMETER_KAFKA_TOPIC}");
    defaultParameters.addArgument(PARAMETER_KAFKA_KEY, "${PARAMETER_KAFKA_KEY}");
    defaultParameters.addArgument(PARAMETER_KAFKA_MESSAGE, "${PARAMETER_KAFKA_MESSAGE}");
    defaultParameters.addArgument(PARAMETER_KAFKA_MESSAGE_SERIALIZER, "org.apache.kafka.common.serialization.StringSerializer");
    defaultParameters.addArgument(PARAMETER_KAFKA_KEY_SERIALIZER, "org.apache.kafka.common.serialization.StringSerializer");
    defaultParameters.addArgument(PARAMETER_KAFKA_SSL_KEYSTORE, "${PARAMETER_KAFKA_SSL_KEYSTORE}");
    defaultParameters.addArgument(PARAMETER_KAFKA_SSL_KEYSTORE_PASSWORD, "${PARAMETER_KAFKA_SSL_KEYSTORE_PASSWORD}");
    defaultParameters.addArgument(PARAMETER_KAFKA_SSL_TRUSTSTORE, "${PARAMETER_KAFKA_SSL_TRUSTSTORE}");
    defaultParameters.addArgument(PARAMETER_KAFKA_SSL_TRUSTSTORE_PASSWORD, "${PARAMETER_KAFKA_SSL_TRUSTSTORE_PASSWORD}");
    defaultParameters.addArgument(PARAMETER_KAFKA_USE_SSL, "${PARAMETER_KAFKA_USE_SSL}");
    defaultParameters.addArgument(PARAMETER_KAFKA_COMPRESSION_TYPE, null);
    defaultParameters.addArgument(PARAMETER_KAFKA_PARTITION, null);
    return defaultParameters;
  }

  @Override
  public SampleResult runTest(JavaSamplerContext context) {
    SampleResult result = newSampleResult();
    String topic = context.getParameter(PARAMETER_KAFKA_TOPIC);
    String key = context.getParameter(PARAMETER_KAFKA_KEY);
    String message = context.getParameter(PARAMETER_KAFKA_MESSAGE);
    sampleResultStart(result, message);

    final ProducerRecord<String, String> producerRecord;
    String partitionString = context.getParameter(PARAMETER_KAFKA_PARTITION);
    if (Strings.isNullOrEmpty(partitionString)) {
      producerRecord = new ProducerRecord<String, String>(topic, key, message);
    } else {
      final int partitionNumber = Integer.parseInt(partitionString);
      producerRecord = new ProducerRecord<String, String>(topic, partitionNumber, key, message);
    }

    try {
      producer.send(producerRecord);
      sampleResultSuccess(result, null);
    } catch (Exception e) {
      sampleResultFailed(result, "500", e);
    }
    return result;
  }

  /**
   * Use UTF-8 for encoding of strings
   */
  private static final String ENCODING = "UTF-8";

  /**
   * Factory for creating new {@link SampleResult}s.
   */
  private SampleResult newSampleResult() {
    SampleResult result = new SampleResult();
    result.setDataEncoding(ENCODING);
    result.setDataType(SampleResult.TEXT);
    return result;
  }

  /**
   * Start the sample request and set the {@code samplerData} to {@code data}.
   *
   * @param result
   *          the sample result to update
   * @param data
   *          the request to set as {@code samplerData}
   */
  private void sampleResultStart(SampleResult result, String data) {
    result.setSamplerData(data);
    result.sampleStart();
  }

  /**
   * Mark the sample result as {@code end}ed and {@code successful} with an "OK" {@code responseCode},
   * and if the response is not {@code null} then set the {@code responseData} to {@code response},
   * otherwise it is marked as not requiring a response.
   *
   * @param result sample result to change
   * @param response the successful result message, may be null.
   */
  private void sampleResultSuccess(SampleResult result, /* @Nullable */ String response) {
    result.sampleEnd();
    result.setSuccessful(true);
    result.setResponseCodeOK();
    if (response != null) {
      result.setResponseData(response, ENCODING);
    }
    else {
      result.setResponseData("No response required", ENCODING);
    }
  }

  /**
   * Mark the sample result as @{code end}ed and not {@code successful}, and set the
   * {@code responseCode} to {@code reason}.
   *
   * @param result the sample result to change
   * @param reason the failure reason
   */
  private void sampleResultFailed(SampleResult result, String reason) {
    result.sampleEnd();
    result.setSuccessful(false);
    result.setResponseCode(reason);
  }

  /**
   * Mark the sample result as @{code end}ed and not {@code successful}, set the
   * {@code responseCode} to {@code reason}, and set {@code responseData} to the stack trace.
   *
   * @param result the sample result to change
   * @param exception the failure exception
   */
  private void sampleResultFailed(SampleResult result, String reason, Exception exception) {
    sampleResultFailed(result, reason);
    result.setResponseMessage("Exception: " + exception);
    result.setResponseData(getStackTrace(exception), ENCODING);
  }

  /**
   * Return the stack trace as a string.
   *
   * @param exception the exception containing the stack trace
   * @return the stack trace
   */
  private String getStackTrace(Exception exception) {
    StringWriter stringWriter = new StringWriter();
    exception.printStackTrace(new PrintWriter(stringWriter));
    return stringWriter.toString();
  }
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>kafkameter</groupId>
    <artifactId>kafkameter</artifactId>
    <version>0.2.0</version>

    <name>kafkameter</name>
    <description>Kafka plugin for JMeter</description>
    <url>http://signal.co</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
        <jdk.version>1.6</jdk.version>
    </properties>

    <dependencies>

        <!-- Base Dependencies -->

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>14.0</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.findbugs</groupId>
            <artifactId>jsr305</artifactId>
            <version>1.3.9</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.2.4</version>
        </dependency>

        <!-- JMeter Dependencies -->

        <dependency>
            <groupId>org.apache.jmeter</groupId>
            <artifactId>ApacheJMeter_core</artifactId>
            <version>2.11</version>
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                    <!-- XXX 2.5.1 included transitively but not available in Maven -->
                    <groupId>com.fifesoft</groupId>
                    <artifactId>rsyntaxtextarea</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.jmeter</groupId>
            <artifactId>ApacheJMeter_java</artifactId>
            <version>2.11</version>
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                    <!-- XXX 2.5.1 included transitively but not available in Maven -->
                    <groupId>com.fifesoft</groupId>
                    <artifactId>rsyntaxtextarea</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Kafka Dependencies -->

        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>0.9.0.1</version>
            <exclusions>
                <!-- XXX Transitively includes log4j 1.2.15 which has bad metadata
                     http://stackoverflow.com/a/9047963 -->
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <source>${jdk.version}</source>
                    <target>${jdk.version}</target>
                    <optimize>true</optimize>
                    <showDeprecation>true</showDeprecation>
                    <showWarnings>true</showWarnings>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

如果不想自己写,也可以在这里下载:
之后补充

补充request信息
在这里插入图片描述
使用csv文件作为数据源
在这里插入图片描述
设置csv文件传送数据,如果第一行就是数据,则需要补充variable name(为csv每列数据添加列名)如果第一行是每列变量名则可以忽略添加variable name
在这里插入图片描述

3.kafka缓冲

在大数据集群上部署kafka集群即可,在启动jmeter之前需要先保证:
1.kafka已经启动
2.已经创建了first主题

4.flink实时处理

将kafka上采集到数据后转存至mysql数据库中
项目结构:
在这里插入图片描述
Load对象:

public class Load {
    private int XH;
    private String CX,SFZRKMC,SFZCKMC,CP;
    private String CKSJ,RKSJ;

    public int getXH() {
        return XH;
    }
    public void setXH(int XH) {
        this.XH = XH;
    }
    public String getCKSJ(){
        return CKSJ;
    }
    public void setCKSJ(String CKSJ){
        this.CKSJ=CKSJ;
    }
    public String getCX() {
        return CX;
    }
    public void setCX(String CX) {
        this.CX = CX;
    }
    public String getSFZRKMC() {
        return SFZRKMC;
    }
    public void setSFZRKMC(String SFZRKMC) {
        this.SFZRKMC = SFZRKMC;
    }
    public String getCP() {
        return CP;
    }
    public void setCP(String CP) {
        this.CP = CP;
    }
    public String getSFZCKMC() {
        return SFZCKMC;
    }
    public void setSFZCKMC(String SFZCKMC) {
        this.SFZCKMC = SFZCKMC;
    }
    public String getRKSJ(){
        return RKSJ;
    }
    public void setRKSJ(String RKSJ){
        this.RKSJ=RKSJ;
    }
}

flink处理:
连接kafka配置信息:

Properties prop = new Properties();
prop.put("bootstrap.servers", "hadoop102:9092");
prop.put("zookeeper.connect", "hadoop102:2181");
prop.put("group.id", "first");
//添加key和value的序列化器
prop.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
prop.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
prop.put("auto.offset.reset", "latest");
DataStream<String> inputStream = env.addSource(new FlinkKafkaConsumer<String>("first", new SimpleStringSchema(), prop));

使用flatmap算子来自处理kafka 9092端口数据(flink source),即将每一条数据转为一个load对象:

DataStream<Load> dataStream =dataStreamSource.flatMap(new FlatMapFunction<String, Load>() {
           @Override
             public void flatMap(String line, Collector<Load> out) throws Exception {
                 String[] tokens = line.split(",");
                 Load Load=new Load();
                 if(tokens.length==7){
                     Load.setXH(Integer.parseInt(tokens[0]));
                     Load.setCKSJ(tokens[1]);
                     Load.setCX(tokens[2]);
                     Load.setRKSJ(tokens[4]);
                     Load.setCP(tokens[6]);
                     Load.setSFZRKMC(tokens[3]);
                     Load.setSFZCKMC(tokens[5]);
                 }
                 out.collect(Load);
             }
         });

收集1s的数据(对象)并将数据发送到sink端口:

 dataStream.timeWindowAll(Time.seconds(1L)).apply(new AllWindowFunction<Load, List<Load>, TimeWindow>() {
                    @Override
                    public void apply(TimeWindow timeWindow, Iterable<Load> iterable, Collector<List<Load>>out) throws Exception {
                        List<Load> Loads = Lists.newArrayList(iterable);
                        if(Loads.size() > 0) {
                            System.out.println("1秒的总共收到的条数:" + Loads.size());
                            out.collect(Loads);
                        }
                    }
                })
                //sink 到数据库
                .addSink(new MysqlConnect());

获取MyCat TESTDB数据库的连接对象:

public class Mysql {

    private static DruidDataSource dataSource;

    public static Connection getConnection() throws Exception {
        dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://hadoop:8066/TESTDB?useUnicode=true&characterEncoding=utf8");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        //设置初始化连接数,最大连接数,最小闲置数
        dataSource.setInitialSize(10);
        dataSource.setMaxActive(75);
        dataSource.setMinIdle(20);
        return  dataSource.getConnection();
    }
}

设置插入对象的数据,并执行SQL插入(flink sink):

public class MysqlConnect extends RichSinkFunction<List<Load>> {

    private PreparedStatement ps;
    private Connection connection;
    private static DruidDataSource dataSource;
    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        //获取数据库连接,准备写入数据库
        connection = Mysql.getConnection();
        String sql = "insert into expressway(XH,CKSJ,CX,SFZRKMC,RKSJ,SFZCKMC,CP) values (?,?,?,?,?,?,?); ";
        ps = connection.prepareStatement(sql);
    }
    @Override
    public void close() throws Exception {
        super.close();
        //关闭并释放资源
        if(connection != null) {
            connection.close();
        }

        if(ps != null) {
            ps.close();
        }
    }
//XH,CX,SFZRKMC,SFZCKMC,CP,从flink处理好的数据拿取
    @Override
    public void invoke(List<Load> Loads, Context context) throws Exception {
        for(Load Load : Loads) {
            ps.setInt(1, Load.getXH());
            ps.setString(2, Load.getCKSJ());
            ps.setString(3, Load.getCX());
            ps.setString(4, Load.getSFZRKMC());
            ps.setString(5,Load.getRKSJ());
            ps.setString(6, Load.getSFZCKMC());
            ps.setString(7, Load.getCP());
            ps.executeUpdate();
        }
    }

}

5.mycat+mysql存放数据

用于存放数据,通过mycat作为mysql中间件进行全局查询和插入,mysql作为实际的存储
如果遇到了mycat的问题=》点下这里
MyCat中以XH%3进行分片(XH为序号)
schema.xml
在这里插入图片描述
rule.xml
在这里插入图片描述

6.flask+ajax+echarts可视化展示

可视化系统项目结构:
在这里插入图片描述

1.static目录中放入json格式的数据文件,和层叠样式表
2.Templates目录下放置可视化页面index.html
3.car_city.py程序用于爬取车牌对应城市信息,爬取信息存储到city.json文件中
4.city_jinwei.py程序用于爬取各城市对应的经纬度,爬取的信息存储到jinwei.text文件中
5.product.py程序用于从数据库中查询数据并将查询结果转换为echarts图表数据格式后存储到data.json文件中
6.server.py程序用于对外提供服务
Web前端核心代码:

//每秒调用一次loadXMLDoc
setInterval(loadXMLDoc,1000)

//ajax请求json文件数据
function loadXMLDoc() {
      var xmlhttp;
      if (window.XMLHttpRequest)
      {
        // IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
        xmlhttp=new XMLHttpRequest();
      }
      else
      {
        // IE6, IE5 浏览器执行代码
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
      }
      xmlhttp.onreadystatechange=function()
      {
        if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
          var myArr = JSON.parse(this.responseText);
          myFunction(myArr)
        }
      }
      xmlhttp.open("GET","../static/data.json",true);
      xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
      xmlhttp.send();
}
//echarts图表实例:
var option5= {
  title: {
    text: '全国城市车辆出入深圳数量分布',
    subtext:'当前数据条数:'+result[7],
    textStyle: {
      color: 'blue',fontSize:34
    },left:'center',subtextStyle:{
      color: '#696969',fontSize:25,fontFamily:'bold'
    },sublink:'https://opendata.sz.gov.cn/data/dataSet/toDataDetails/29200_00403621'
  },
  legend: {
    data: ['全国各地车辆数'], //与series的name属性对应
    orient: 'vertical',
    y: 'top',
    x: 'right',
    textStyle: {
      color: 'LightSteelBlue',fontSize:25
    }
  },
  tooltip: {
    trigger: 'item',
    formatter: function(params) {
      return params.name + ' : ' + params.value[2];
    }
    ,textStyle:{fontSize:30}
  },
  visualMap: {
    min: 0,
    max: 2000,
    calculable: true,
    inRange: {
      color: ['#54FF9F','Blue', 'yellow', 'red']
    },
    textStyle: {
      color: 'Cyan',fontSize:25
    }
  },
  geo: {
    map: 'china',
    roam: true, //开启鼠标缩放和漫游
    zoom: 1, //地图缩放级别
    selectedMode: false, //选中模式:single | multiple
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    // layoutCenter: ['50%', '50%'], //设置后left/right/top/bottom等属性无效
    layoutSize: '120%',
    label: {
      emphasis: {
        show: false
      }
    },
    itemStyle: {
      normal: {
        areaColor: 'black',
        borderWidth: 1.1,
        borderColor: '#43d0d6'
      },
      emphasis: {
        areaColor: '#069'
      }
    }
  },
  series: [{
    name: '全国各地车辆数',
    type: 'scatter',
    coordinateSystem: 'geo',
    symbolSize: 12,
    label: {
      normal: {
        show: false
      },
      emphasis: {
        show: false
      }
    },
    data: convertScatterData(scatterVal)
  }]
};

Web服务端核心代码分析:

from flask import Flask,render_template
app = Flask(__name__)  # 创建Flask实例
@app.route("/index",methods=['GET'])
def rount():
    return render_template('index.html')
if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)

设置了一个路由’/index’,并设置 host为0.0.0.0,使得其他客户端可以 访问到该网站,port端口为5000。
正确运行时:
在这里插入图片描述
web页面1:
在这里插入图片描述
web页面2是我们主项目,web页面1是我无聊写的
web页面2:
在这里插入图片描述


小结

缺少对业务的理解,有些图表只是为了可视化而可视化,没有考虑到它真正的用途。从实际出发,这样才更有利于在面对未来正式的业务需求时能够从容面对。未来会尝试做一些企业项目来提升自己的业务能力。

  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看不见的罗辑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值