vue+d3js+fastapi實(shí)現(xiàn)天氣柱狀圖折線圖餅圖的示例
說明:
vue+d3js+fastapi實(shí)現(xiàn)天氣柱狀圖折線圖餅圖
效果圖:
step0:postman
1. 生成天氣數(shù)據(jù)(POST請求):
URL: http://localhost:8000/generate-data/?year=2024&month=3&seed=42
方法: POST
Headers: Content-Type: application/json
成功響應(yīng)示例:
{ "status": "success", "message": "成功生成31條天氣數(shù)據(jù)", "year": 2024, "month": 3 }
2. 查詢天氣數(shù)據(jù)(GET請求):
URL: http://localhost:8000/weather-data/?year=2024&month=4
方法: GET
成功響應(yīng)示例:
{ "status": "success", "count": 31, "year": 2024, "month": 3, "data": [ { "record_date": "2024-03-01", "temperature": 16.4, "humidity": 72, "precipitation": 0.0, "wind_speed": 7.2, "weather_condition": "Cloudy" }, { "record_date": "2024-03-31", "temperature": 17.3, "humidity": 62, "precipitation": 3.8, "wind_speed": 1.4, "weather_condition": "Rain" } ] }
step1:sql
CREATE TABLE weather_data ( id INT AUTO_INCREMENT PRIMARY KEY, record_date DATE NOT NULL, temperature DECIMAL(4,1) NOT NULL, -- 格式:-99.9 到 99.9 humidity TINYINT UNSIGNED NOT NULL, -- 范圍:0-100 precipitation DECIMAL(5,1) NOT NULL, -- 最大999.9mm wind_speed DECIMAL(4,1) NOT NULL, -- 最大99.9m/s weather_condition VARCHAR(50) NOT NULL, -- 修改列名 INDEX (record_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; select *from weather_data;
step2:python test
C:\Users\wangrusheng\PycharmProjects\FastAPIProject1\hello.py
import random from datetime import date from decimal import Decimal import calendar import pymysql.cursors import json # 數(shù)據(jù)庫配置(根據(jù)實(shí)際情況修改) DB_CONFIG = { 'host': 'localhost', 'user': 'root', 'password': '123456', 'db': 'db_school', 'charset': 'utf8mb4', 'cursorclass': pymysql.cursors.DictCursor } def generate_temperature(rng, min_temp=10.0, max_temp=20.0): """生成溫度數(shù)據(jù)(均勻分布)""" temp = rng.uniform(min_temp, max_temp) return round(temp, 1) def generate_humidity(rng): """生成濕度數(shù)據(jù)(正態(tài)分布)""" humidity = rng.gauss(60, 15) humidity = max(0, min(humidity, 100)) return int(round(humidity)) def generate_precipitation(rng): """生成降水量數(shù)據(jù)(20%概率下雨)""" if rng.random() < 0.2: amount = rng.expovariate(1 / 5.0) # 平均5mm amount = max(0.1, min(amount, 30.0)) return round(amount, 1) return 0.0 def generate_wind_speed(rng): """生成風(fēng)速數(shù)據(jù)(伽馬分布)""" speed = rng.gammavariate(2, 2) speed = max(0.0, min(speed, 20.0)) return round(speed, 1) def get_weather_condition(temperature, precipitation, humidity, rng): """根據(jù)天氣參數(shù)判斷天氣狀況""" if precipitation > 0: return 'Snow' if temperature < 3.0 else 'Rain' if humidity >= 70: return 'Cloudy' if humidity <= 30: return 'Sunny' return rng.choice(['Partly Cloudy', 'Mostly Cloudy']) def generate_monthly_weather_data(year, month, rng=None): """生成整月天氣數(shù)據(jù)""" if rng is None: rng = random.Random() _, num_days = calendar.monthrange(year, month) data = [] for day in range(1, num_days + 1): record_date = date(year, month, day) temperature = generate_temperature(rng) humidity = generate_humidity(rng) precipitation = generate_precipitation(rng) wind_speed = generate_wind_speed(rng) condition = get_weather_condition( temperature, precipitation, humidity, rng ) data.append({ 'record_date': record_date, 'temperature': temperature, 'humidity': humidity, 'precipitation': precipitation, 'wind_speed': wind_speed, 'weather_condition': condition }) return data def insert_weather_data(data): """批量插入天氣數(shù)據(jù)到數(shù)據(jù)庫""" connection = pymysql.connect(**DB_CONFIG) try: with connection.cursor() as cursor: sql = """ INSERT INTO weather_data (record_date, temperature, humidity, precipitation, wind_speed, weather_condition) VALUES (%s, %s, %s, %s, %s, %s) """ params = [ ( d['record_date'], d['temperature'], d['humidity'], d['precipitation'], d['wind_speed'], d['weather_condition'] ) for d in data ] cursor.executemany(sql, params) connection.commit() return len(data) except Exception as e: connection.rollback() raise e finally: connection.close() def get_weather_data(year: int, month: int) -> list: """從數(shù)據(jù)庫獲取指定年月的天氣數(shù)據(jù)并轉(zhuǎn)換為JSON兼容格式""" connection = pymysql.connect(**DB_CONFIG) try: with connection.cursor() as cursor: sql = """ SELECT record_date, temperature, humidity, precipitation, wind_speed, weather_condition FROM weather_data WHERE YEAR(record_date) = %s AND MONTH(record_date) = %s ORDER BY record_date """ cursor.execute(sql, (year, month)) results = cursor.fetchall() # 轉(zhuǎn)換日期和數(shù)值類型 for record in results: record['record_date'] = record['record_date'].isoformat() # 處理Decimal類型(如果存在) for key in ['temperature', 'precipitation', 'wind_speed']: if isinstance(record[key], Decimal): record[key] = float(record[key]) return results finally: connection.close() if __name__ == '__main__': # 示例:生成并插入2024年4月的天氣數(shù)據(jù) year = 2024 month = 4 # 創(chuàng)建帶種子的隨機(jī)生成器(保證結(jié)果可復(fù)現(xiàn)) rng = random.Random(42) try: # 生成模擬數(shù)據(jù) weather_data = generate_monthly_weather_data(year, month, rng) # 插入數(shù)據(jù)庫 # inserted_count = insert_weather_data(weather_data) # print(f"成功插入{inserted_count}條天氣數(shù)據(jù)") # 獲取并打印JSON數(shù)據(jù) weather_json = get_weather_data(year, month) print(json.dumps(weather_json, indent=2, ensure_ascii=False)) except Exception as e: print(f"操作失敗: {str(e)}")
step3:python fastapi
C:\Users\wangrusheng\PycharmProjects\FastAPIProject1\main.py
from fastapi import FastAPI, HTTPException, Query from datetime import date from decimal import Decimal from typing import Optional import random import calendar import pymysql.cursors import json from fastapi.middleware.cors import CORSMiddleware app = FastAPI() # CORS配置 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 數(shù)據(jù)庫配置(根據(jù)實(shí)際情況修改) DB_CONFIG = { 'host': 'localhost', 'user': 'root', 'password': '123456', 'db': 'db_school', 'charset': 'utf8mb4', 'cursorclass': pymysql.cursors.DictCursor } # 以下保持原有函數(shù)定義不變(generate_temperature、generate_humidity等) # [原有函數(shù)定義區(qū),保持與問題中完全相同的函數(shù)實(shí)現(xiàn)] def generate_temperature(rng, min_temp=10.0, max_temp=20.0): """生成溫度數(shù)據(jù)(均勻分布)""" temp = rng.uniform(min_temp, max_temp) return round(temp, 1) def generate_humidity(rng): """生成濕度數(shù)據(jù)(正態(tài)分布)""" humidity = rng.gauss(60, 15) humidity = max(0, min(humidity, 100)) return int(round(humidity)) def generate_precipitation(rng): """生成降水量數(shù)據(jù)(20%概率下雨)""" if rng.random() < 0.2: amount = rng.expovariate(1 / 5.0) # 平均5mm amount = max(0.1, min(amount, 30.0)) return round(amount, 1) return 0.0 def generate_wind_speed(rng): """生成風(fēng)速數(shù)據(jù)(伽馬分布)""" speed = rng.gammavariate(2, 2) speed = max(0.0, min(speed, 20.0)) return round(speed, 1) def get_weather_condition(temperature, precipitation, humidity, rng): """根據(jù)天氣參數(shù)判斷天氣狀況""" if precipitation > 0: return 'Snow' if temperature < 3.0 else 'Rain' if humidity >= 70: return 'Cloudy' if humidity <= 30: return 'Sunny' return rng.choice(['Partly Cloudy', 'Mostly Cloudy']) def generate_monthly_weather_data(year, month, rng=None): """生成整月天氣數(shù)據(jù)""" if rng is None: rng = random.Random() _, num_days = calendar.monthrange(year, month) data = [] for day in range(1, num_days + 1): record_date = date(year, month, day) temperature = generate_temperature(rng) humidity = generate_humidity(rng) precipitation = generate_precipitation(rng) wind_speed = generate_wind_speed(rng) condition = get_weather_condition( temperature, precipitation, humidity, rng ) data.append({ 'record_date': record_date, 'temperature': temperature, 'humidity': humidity, 'precipitation': precipitation, 'wind_speed': wind_speed, 'weather_condition': condition }) return data def insert_weather_data(data): """批量插入天氣數(shù)據(jù)到數(shù)據(jù)庫""" connection = pymysql.connect(**DB_CONFIG) try: with connection.cursor() as cursor: sql = """ INSERT INTO weather_data (record_date, temperature, humidity, precipitation, wind_speed, weather_condition) VALUES (%s, %s, %s, %s, %s, %s) """ params = [ ( d['record_date'], d['temperature'], d['humidity'], d['precipitation'], d['wind_speed'], d['weather_condition'] ) for d in data ] cursor.executemany(sql, params) connection.commit() return len(data) except Exception as e: connection.rollback() raise e finally: connection.close() def get_weather_data(year: int, month: int) -> list: """從數(shù)據(jù)庫獲取指定年月的天氣數(shù)據(jù)并轉(zhuǎn)換為JSON兼容格式""" connection = pymysql.connect(**DB_CONFIG) try: with connection.cursor() as cursor: sql = """ SELECT record_date, temperature, humidity, precipitation, wind_speed, weather_condition FROM weather_data WHERE YEAR(record_date) = %s AND MONTH(record_date) = %s ORDER BY record_date """ cursor.execute(sql, (year, month)) results = cursor.fetchall() # 轉(zhuǎn)換日期和數(shù)值類型 for record in results: record['record_date'] = record['record_date'].isoformat() # 處理Decimal類型(如果存在) for key in ['temperature', 'precipitation', 'wind_speed']: if isinstance(record[key], Decimal): record[key] = float(record[key]) return results finally: connection.close() @app.post("/generate-data/") async def generate_weather_data( year: int = Query(..., ge=2000, le=2100, description="年份"), month: int = Query(..., ge=1, le=12, description="月份"), seed: Optional[int] = Query(None, description="隨機(jī)種子(可選)") ): """生成并插入指定月份的天氣數(shù)據(jù)""" try: rng = random.Random(seed) if seed else random.Random() weather_data = generate_monthly_weather_data(year, month, rng) inserted_count = insert_weather_data(weather_data) return { "status": "success", "message": f"成功生成{inserted_count}條天氣數(shù)據(jù)", "year": year, "month": month } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"數(shù)據(jù)庫操作失敗: {str(e)}") @app.get("/weather-data/") async def get_weather( year: int = Query(..., ge=2000, le=2100, description="年份"), month: int = Query(..., ge=1, le=12, description="月份") ): """獲取指定月份的天氣數(shù)據(jù)""" try: data = get_weather_data(year, month) return { "status": "success", "count": len(data), "year": year, "month": month, "data": data } except Exception as e: raise HTTPException(status_code=500, detail=f"數(shù)據(jù)查詢失敗: {str(e)}") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)
step4:vue
C:\Users\wangrusheng\PycharmProjects\untitled3\src\views\Lottery.vue
<template> <div> <div class="controls"> <input v-model.number="year" type="number" placeholder="年份"> <input v-model.number="month" type="number" placeholder="月份" min="1" max="12"> <button @click="fetchData">查詢</button> <button @click="generateData">生成數(shù)據(jù)</button> </div> <div class="charts-container"> <div class="chart-box"> <h3>每日溫度柱狀圖</h3> <div ref="barChart" class="chart"></div> </div> <div class="chart-box"> <h3>溫度趨勢折線圖</h3> <div ref="lineChart" class="chart"></div> </div> <div class="chart-box"> <h3>天氣狀況分布餅圖</h3> <div ref="pieChart" class="chart"></div> </div> </div> </div> </template> <script> import * as d3 from 'd3'; import axios from 'axios'; export default { data() { return { weatherData: [], year: null, month: null }; }, methods: { async fetchData() { if (!this.validateInput()) return; try { const response = await axios.get('http://localhost:8000/weather-data/', { params: { year: this.year, month: this.month } }); this.weatherData = response.data.data; this.redrawCharts(); } catch (error) { this.handleError(error, '查詢'); } }, async generateData() { if (!this.validateInput()) return; try { const response = await axios.post('http://localhost:8000/generate-data/', null, { params: { year: this.year, month: this.month, seed: 42 }, headers: { 'Content-Type': 'application/json' } }); alert(`生成成功:${response.data.message}`); await this.fetchData(); } catch (error) { this.handleError(error, '生成'); } }, validateInput() { if (!this.year || !this.month) { alert('請?zhí)顚懩攴莺驮路?); return false; } if (this.month < 1 || this.month > 12) { alert('月份必須為1-12'); return false; } return true; }, handleError(error, operation) { console.error(`${operation}失敗:`, error); alert(`${operation}失敗,請檢查控制臺(tái)`); }, redrawCharts() { this.clearCharts(); this.drawBarChart(); this.drawLineChart(); this.drawPieChart(); }, clearCharts() { [this.$refs.barChart, this.$refs.lineChart, this.$refs.pieChart] .forEach(ref => ref.innerHTML = ''); }, // 各圖表繪制方法(保持原有實(shí)現(xiàn),開頭添加清除邏輯) // 繪制柱狀圖 drawBarChart() { const margin = { top: 30, right: 30, bottom: 50, left: 60 }; const width = 800 - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const svg = d3.select(this.$refs.barChart) .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', `translate(${margin.left},${margin.top})`); // 創(chuàng)建比例尺 const x = d3.scaleBand() .domain(this.weatherData.map(d => d.record_date)) .range([0, width]) .padding(0.2); const y = d3.scaleLinear() .domain([0, d3.max(this.weatherData, d => d.temperature)]) .range([height, 0]); // 添加柱狀 svg.selectAll("rect") .data(this.weatherData) .join("rect") .attr("x", d => x(d.record_date)) .attr("y", d => y(d.temperature)) .attr("width", x.bandwidth()) .attr("height", d => height - y(d.temperature)) .attr("fill", "#4CAF50"); // 添加坐標(biāo)軸 svg.append("g") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5)))); svg.append("g") .call(d3.axisLeft(y)); // 添加標(biāo)簽 svg.append("text") .attr("transform", `translate(${width/2}, ${height + 40})`) .style("text-anchor", "middle") .text("日期"); svg.append("text") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left) .attr("x",0 - (height / 2)) .attr("dy", "1em") .style("text-anchor", "middle") .text("溫度(℃)"); }, // 繪制折線圖 drawLineChart() { const margin = { top: 30, right: 30, bottom: 50, left: 60 }; const width = 800 - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const svg = d3.select(this.$refs.lineChart) .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', `translate(${margin.left},${margin.top})`); // 創(chuàng)建比例尺 const x = d3.scaleBand() .domain(this.weatherData.map(d => d.record_date)) .range([0, width]); const y = d3.scaleLinear() .domain([d3.min(this.weatherData, d => d.temperature) - 2, d3.max(this.weatherData, d => d.temperature) + 2]) .range([height, 0]); // 創(chuàng)建折線生成器 const line = d3.line() .x(d => x(d.record_date) + x.bandwidth()/2) .y(d => y(d.temperature)); // 繪制折線 svg.append("path") .datum(this.weatherData) .attr("fill", "none") .attr("stroke", "#2196F3") .attr("stroke-width", 2) .attr("d", line); // 添加坐標(biāo)軸 svg.append("g") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5)))); svg.append("g") .call(d3.axisLeft(y)); }, // 繪制餅圖 drawPieChart() { const width = 400; const height = 400; const radius = Math.min(width, height) / 2; const svg = d3.select(this.$refs.pieChart) .append('svg') .attr('width', width) .attr('height', height) .append('g') .attr('transform', `translate(${width/2},${height/2})`); // 統(tǒng)計(jì)天氣狀況 const data = Array.from( d3.rollup(this.weatherData, v => v.length, d => d.weather_condition ), ([name, value]) => ({name, value}) ); // 創(chuàng)建顏色比例尺 const color = d3.scaleOrdinal() .domain(data.map(d => d.name)) .range(d3.schemeCategory10); // 餅圖生成器 const pie = d3.pie() .value(d => d.value); // 弧形生成器 const arc = d3.arc() .innerRadius(0) .outerRadius(radius); // 繪制扇形 const arcs = svg.selectAll("arc") .data(pie(data)) .enter() .append("g") .attr("class", "arc"); arcs.append("path") .attr("d", arc) .attr("fill", d => color(d.data.name)) .attr("stroke", "white") .style("stroke-width", "2px"); // 添加標(biāo)簽 arcs.append("text") .attr("transform", d => `translate(${arc.centroid(d)})`) .attr("text-anchor", "middle") .text(d => d.data.name); } } }; </script> <style> .controls { padding: 1rem; display: flex; gap: 1rem; align-items: center; } .controls input { padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; width: 120px; } .controls button { padding: 0.5rem 1rem; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; } .controls button:hover { background: #1976D2; } .charts-container { display: flex; flex-direction: column; gap: 2rem; padding: 2rem; } .chart-box { background: white; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); width: 100%; } .chart-box h3 { margin: 0 0 1rem; color: #333; } .chart { width: 100%; height: 400px; } </style>
end
//我是分割線
step101: old vue
下面的代碼修改: 1.年份 月份 改為可選, 2.新增兩個(gè)按鈕,查詢和添加 3.查詢和添加,需要做網(wǎng)絡(luò)請求 4.三個(gè)圖表 需要垂直排列 1. 生成天氣數(shù)據(jù)(POST請求): URL: http://localhost:8000/generate-data/?year=2024&month=3&seed=42 方法: POST Headers: Content-Type: application/json 成功響應(yīng)示例: { "status": "success", "message": "成功生成31條天氣數(shù)據(jù)", "year": 2024, "month": 3 } <template> <div class="charts-container"> <div class="chart-box"> <h3>每日溫度柱狀圖</h3> <div ref="barChart" class="chart"></div> </div> <div class="chart-box"> <h3>溫度趨勢折線圖</h3> <div ref="lineChart" class="chart"></div> </div> <div class="chart-box"> <h3>天氣狀況分布餅圖</h3> <div ref="pieChart" class="chart"></div> </div> </div> </template> <script> import * as d3 from 'd3'; import axios from 'axios'; export default { data() { return { weatherData: [] }; }, async mounted() { try { const response = await axios.get('http://localhost:8000/weather-data/?year=2024&month=4'); this.weatherData = response.data.data; this.drawBarChart(); this.drawLineChart(); this.drawPieChart(); } catch (error) { console.error('數(shù)據(jù)獲取失敗:', error); } }, methods: { // 繪制柱狀圖 drawBarChart() { const margin = { top: 30, right: 30, bottom: 50, left: 60 }; const width = 800 - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const svg = d3.select(this.$refs.barChart) .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', `translate(${margin.left},${margin.top})`); // 創(chuàng)建比例尺 const x = d3.scaleBand() .domain(this.weatherData.map(d => d.record_date)) .range([0, width]) .padding(0.2); const y = d3.scaleLinear() .domain([0, d3.max(this.weatherData, d => d.temperature)]) .range([height, 0]); // 添加柱狀 svg.selectAll("rect") .data(this.weatherData) .join("rect") .attr("x", d => x(d.record_date)) .attr("y", d => y(d.temperature)) .attr("width", x.bandwidth()) .attr("height", d => height - y(d.temperature)) .attr("fill", "#4CAF50"); // 添加坐標(biāo)軸 svg.append("g") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5)))); svg.append("g") .call(d3.axisLeft(y)); // 添加標(biāo)簽 svg.append("text") .attr("transform", `translate(${width/2}, ${height + 40})`) .style("text-anchor", "middle") .text("日期"); svg.append("text") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left) .attr("x",0 - (height / 2)) .attr("dy", "1em") .style("text-anchor", "middle") .text("溫度(℃)"); }, // 繪制折線圖 drawLineChart() { const margin = { top: 30, right: 30, bottom: 50, left: 60 }; const width = 800 - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const svg = d3.select(this.$refs.lineChart) .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', `translate(${margin.left},${margin.top})`); // 創(chuàng)建比例尺 const x = d3.scaleBand() .domain(this.weatherData.map(d => d.record_date)) .range([0, width]); const y = d3.scaleLinear() .domain([d3.min(this.weatherData, d => d.temperature) - 2, d3.max(this.weatherData, d => d.temperature) + 2]) .range([height, 0]); // 創(chuàng)建折線生成器 const line = d3.line() .x(d => x(d.record_date) + x.bandwidth()/2) .y(d => y(d.temperature)); // 繪制折線 svg.append("path") .datum(this.weatherData) .attr("fill", "none") .attr("stroke", "#2196F3") .attr("stroke-width", 2) .attr("d", line); // 添加坐標(biāo)軸 svg.append("g") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5)))); svg.append("g") .call(d3.axisLeft(y)); }, // 繪制餅圖 drawPieChart() { const width = 400; const height = 400; const radius = Math.min(width, height) / 2; const svg = d3.select(this.$refs.pieChart) .append('svg') .attr('width', width) .attr('height', height) .append('g') .attr('transform', `translate(${width/2},${height/2})`); // 統(tǒng)計(jì)天氣狀況 const data = Array.from( d3.rollup(this.weatherData, v => v.length, d => d.weather_condition ), ([name, value]) => ({name, value}) ); // 創(chuàng)建顏色比例尺 const color = d3.scaleOrdinal() .domain(data.map(d => d.name)) .range(d3.schemeCategory10); // 餅圖生成器 const pie = d3.pie() .value(d => d.value); // 弧形生成器 const arc = d3.arc() .innerRadius(0) .outerRadius(radius); // 繪制扇形 const arcs = svg.selectAll("arc") .data(pie(data)) .enter() .append("g") .attr("class", "arc"); arcs.append("path") .attr("d", arc) .attr("fill", d => color(d.data.name)) .attr("stroke", "white") .style("stroke-width", "2px"); // 添加標(biāo)簽 arcs.append("text") .attr("transform", d => `translate(${arc.centroid(d)})`) .attr("text-anchor", "middle") .text(d => d.data.name); } } }; </script> <style> .charts-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 2rem; padding: 2rem; } .chart-box { background: white; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .chart-box h3 { margin: 0 0 1rem; color: #333; } .chart { width: 100%; height: 400px; } </style>
step102:c++ 模擬數(shù)據(jù)
C:\Users\wangrusheng\source\repos\CMakeProject1\CMakeProject1\CMakeProject1.cpp
#include <iostream> #include <random> #include <vector> #include <string> #include <iomanip> #include <cmath> struct WeatherData { int day; double temperature; int humidity; double precipitation; double wind_speed; std::string condition; }; // 生成溫度(可指定范圍) double generate_temperature(std::mt19937& gen, double min_temp = 10.0, double max_temp = 20.0) { std::uniform_real_distribution<double> dist(min_temp, max_temp); return std::round(dist(gen) * 10) / 10.0; // 保留1位小數(shù) } // 生成濕度(正態(tài)分布) int generate_humidity(std::mt19937& gen) { std::normal_distribution<double> dist(60.0, 15.0); double humidity = dist(gen); humidity = std::clamp(humidity, 0.0, 100.0); return static_cast<int>(std::round(humidity)); } // 生成降水量(20%概率下雨) double generate_precipitation(std::mt19937& gen) { std::bernoulli_distribution rain_dist(0.2); if (rain_dist(gen)) { std::exponential_distribution<double> amount_dist(1.0 / 5.0); // 平均5mm double amount = amount_dist(gen); amount = std::clamp(amount, 0.1, 30.0); return std::round(amount * 10) / 10.0; // 保留1位小數(shù) } return 0.0; } // 生成風(fēng)速(伽馬分布) double generate_wind_speed(std::mt19937& gen) { std::gamma_distribution<double> dist(2.0, 2.0); double speed = dist(gen); speed = std::clamp(speed, 0.0, 20.0); return std::round(speed * 10) / 10.0; // 保留1位小數(shù) } // 生成天氣狀況 std::string get_condition(double temp, double precip, int humidity) { if (precip > 0) { return (temp < 3.0) ? "Snow" : "Rain"; } if (humidity >= 70) return "Cloudy"; if (humidity <= 30) return "Sunny"; // 隨機(jī)選擇部分多云或陰天 static std::vector<std::string> options = { "Partly Cloudy", "Mostly Cloudy" }; std::uniform_int_distribution<int> dist(0, 1); std::mt19937 temp_gen(std::random_device{}()); return options[dist(temp_gen)]; } // 生成完整月份數(shù)據(jù) std::vector<WeatherData> generate_april_data(std::mt19937& gen) { std::vector<WeatherData> data; for (int day = 1; day <= 30; ++day) { WeatherData wd; wd.day = day; wd.temperature = generate_temperature(gen); wd.humidity = generate_humidity(gen); wd.precipitation = generate_precipitation(gen); wd.wind_speed = generate_wind_speed(gen); wd.condition = get_condition(wd.temperature, wd.precipitation, wd.humidity); data.push_back(wd); } return data; } int main() { // 初始化隨機(jī)數(shù)生成器 std::random_device rd; std::mt19937 gen(rd()); // 生成數(shù)據(jù) auto weather_data = generate_april_data(gen); // 輸出CSV格式 std::cout << "Day,Temperature,Humidity,Precipitation,Wind Speed,Condition\n"; for (const auto& wd : weather_data) { std::cout << wd.day << "," << std::fixed << std::setprecision(1) << wd.temperature << "°C," << wd.humidity << "%," << wd.precipitation << "mm," << wd.wind_speed << "m/s," << wd.condition << "\n"; } return 0; }
到此這篇關(guān)于vue+d3js+fastapi實(shí)現(xiàn)天氣柱狀圖折線圖餅圖的示例的文章就介紹到這了,更多相關(guān)vue+d3js+fastapi柱狀圖折線圖餅圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
適用于 Vue 的播放器組件Vue-Video-Player操作
這篇文章主要介紹了適用于 Vue 的播放器組件Vue-Video-Player操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11vue引入新版 vue-awesome-swiper插件填坑問題
這篇文章主要介紹了vue引入新版 vue-awesome-swiper插件填坑問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01vue3通過ref獲取子組件defineExpose的數(shù)據(jù)和方法
defineExpose是Vue3中新增的選項(xiàng),用于向父組件暴露子組件內(nèi)部的屬性和方法,通過defineExpose,子組件可以主動(dòng)控制哪些屬性和方法可以被父組件訪問,本文主要介紹了vue3通過ref獲取子組件defineExpose的數(shù)據(jù)和方法,需要的朋友可以參考下2023-10-10Composition Api封裝業(yè)務(wù)hook思路示例分享
這篇文章主要為大家介紹了Composition Api封裝業(yè)務(wù)hook的思路示例分享,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07vue-cli中的babel配置文件.babelrc實(shí)例詳解
Babel是一個(gè)廣泛使用的轉(zhuǎn)碼器,可以將ES6代碼轉(zhuǎn)為ES5代碼,從而在現(xiàn)有環(huán)境執(zhí)行。本文介紹vue-cli腳手架工具根目錄的babelrc配置文件,感興趣的朋友一起看看吧2018-02-02Vue 解決父組件跳轉(zhuǎn)子路由后當(dāng)前導(dǎo)航active樣式消失問題
這篇文章主要介紹了Vue 解決父組件跳轉(zhuǎn)子路由后當(dāng)前導(dǎo)航active樣式消失問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07