当前位置:网站首页>Subcontracting loading of wechat applet
Subcontracting loading of wechat applet
2022-07-28 20:46:00 【richest_ qi】
List of articles
Antecedents feed
In some cases , You need to subcontract the applet . There must be a small program after subcontracting Main package , One or more to subcontract .
When the applet starts , Default download Main package And start the page in the main package , Only when entering a page within the subcontract , The client will download the corresponding subcontracting , Then show the subcontracting page , namely to subcontract yes On demand Conduct load Of .
At present, the packet size of small programs has the following restrictions :
- The size of all packets of the whole small program shall not exceed
20M. - Main package or The size of a single subcontract shall not exceed
2M.
Package structure of applet
The package structure of the applet is as follows . among , Folder pages Next is the main package , Folder music-package The next is subcontracting .
app.json Declare package structure
stay app.json Declare the package structure . among , pages Field declares the main package ,subpackages Field declares the subcontracting structure .subpackages The value of is an array , Each element of the array is an object , This object contains the following properties :
root, Root directory of subcontracting ,string type .name, Subcontract alias , Subcontracting pre download can use ,string type .pages, Subcontracting page path , Relative to the subcontracting root directory , That is relative to the root.pagesIs an array of strings .independent, Whether to use independent subcontracting , Boolean type .
Independent subcontracting
Independent subcontracting It is a special type of subcontracting in applet , It can Run independently of the main package and other subcontracts .
A small program can have multiple independent subcontractors .
When the applet is launched from the main package page or other subcontracting pages , You need to download the main package first . When entering the independent subcontracting page , There is no need to download the main package , Because independent subcontracting is independent of the main package and other subcontracts .
Declare independent subcontracting
app.json Medium subpackages Field independent Field , Set to true, That is, independent subcontracting .
Subcontracting pre download
Subcontracting pre download , That is, when entering the applet page , Download the subcontracting that may be required in advance .
Declare subcontracting pre download
stay app.json Of preloadRule I declare that I will subcontract the pre download .preloadRule The value of is an object , The contents of this object are in key-value In the form of . among ,key Is a subcontracting page path ,value It's an object , This object contains the following properties :
packages, Is an array , Each element is pre downloaded and subcontracted after entering the pagerootorname. in addition ,__APP__On behalf of the main package . Such aspackages:["package_a_root","package_b_name","__APP__"].network, Indicates preloading under the specified network .string type , The following two values are supported :"all", Means unlimited network ."wifi", Express , only wifi Pre download .
Build a static resource server
- Global installation serve:
npm install -g serve - Create a new folder wherever you want :
resources.resources I'm gonna go ahead and create a new folder :images, Used to store static image resources ;resources I'm gonna go ahead and create a new folder :videos, For storing video files ;resources I'm gonna go ahead and create a new folder :audios, For storing audio files . - Start the server :
serve resources.
Applet project
The main files involved in the code are :
- app.json
- app.wxss
- app.js
- pages/search/search.json
- pages/search/search.wxml
- pages/search/search.wxss
- pages/search/search.js
- pages/login/login.json
- pages/login/login.wxml
- pages/login/login.wxss
- pages/login/login.js
- music-package/pages/index/index.json
- music-package/pages/index/index.wxml
- music-package/pages/index/index.wxss
- music-package/pages/index/index.js
- music-package/pages/music/music.json
- music-package/pages/music/music.wxml
- music-package/pages/music/music.wxss
- music-package/pages/music/music.js

app.json
{
"pages": [
"pages/search/search",
"pages/login/login"
],
"subPackages": [
{
"root": "music-package",
"pages": [
"pages/index/index",
"pages/music/music"
]
}
],
"window": {
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": " home page ",
"navigationBarTextStyle": "black"
},
"tabBar": {
"position": "top",
"color": "#000000",
"selectedColor": "#971a22",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/search/search",
"text": " Search for music "
},
{
"pagePath": "pages/login/login",
"text": " Personal login "
}
]
},
"requiredBackgroundModes": [
"audio"
],
"style": "v2",
"sitemapLocation": "sitemap.json"
}
Main package
app.wxss
page{
height: 100%;
}
app.js
App({
globalData:{
isPlayGlobal:false, // Is there a song currently playing
musicIdGlobal:'' // Which song is currently playing
}
})
pages/search/search.json
{
"usingComponents": {
},
"navigationBarTitleText": " Search for "
}
pages/search/search.wxml
<view class="search-container">
<view class="header">
<view class="input-box">
<image src="/static/images/search.png"></image>
<input type="text" placeholder=" What you want to hear There are... Here ~" placeholder-style="font-swxize:27rpx" bindinput="handleInput" value="{
{keyword}}"/>
<image src="/static/images/cross.png" bindtap="removeKeyword" hidden="{
{!keyword}}"></image>
</view>
</view>
<block wx:if="{
{keyword}}">
<view class="search-container">
<view class="search-title"> Search for "{
{keyword}}"</view>
<view class="search-list">
<view class="search-item" wx:for="{
{searchList}}" wx:key="id">
<image src="/static/images/search.png"></image>
<view class="content">{
{item.content}}</view>
</view>
</view>
</view>
</block>
<block wx:else>
<view class="history-container" wx:if="{
{historyList.length}}">
<view class="history-header">
<view class="history-title"> Search history </view>
<image src="/static/images/delete.png" bindtap="deleteHistory"></image>
</view>
<view class="history-list">
<text class="history-item" wx:for="{
{historyList}}" wx:key="*this">{
{item}}</text>
</view>
</view>
<view class="hot-container">
<view class="hot-title"> Hot search list </view>
<view class="hot-list">
<view class="hot-item" wx:for="{
{hotList}}" wx:key="id">
<text class="order" style="{
{
(index===0 || index ===1 || index==2) && 'color:#d81e06' }}">{
{index+1}}</text>
<text class="name">{
{item.keyword}}</text>
<image wx:if="{
{item.iconUrl}}" src="{
{item.iconUrl}}"></image>
</view>
</view>
</view>
</block>
</view>
pages/search/search.wxss
.search-container{
padding: 20rpx;
}
.input-box{
background: #eee;
border-radius: 28rpx;
display: flex;
align-items: center;
}
.input-box input{
height: 60rpx;
line-height: 60rpx;
flex: 1;
font-size: 27rpx;
}
.input-box image{
width: 36rpx;
height: 36rpx;
padding: 0 20rpx;
}
.hot-container{
margin: 20rpx 0;
}
.hot-container .hot-title{
font-size: 26rpx;
font-weight:550;
}
.hot-list{
padding: 10rpx 0 ;
}
.hot-item{
height: 60rpx;
line-height: 60rpx;
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.hot-item .order{
display: inline-block;
width: 40rpx;
height: 60rpx;
line-height: 60rpx;
text-align: center;
margin-right: 30rpx;
color: #888;
}
.hot-item .name{
font-weight: 550;
}
.hot-item image{
width: 48rpx;
height: 48rpx;
margin-left: 20rpx;
}
.search-container .search-title{
color: #d81e06;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
}
.search-item{
display: flex;
align-items: center;
height: 80rpx;
line-height: 80rpx;
}
.search-item image{
width: 28rpx;
height: 28rpx;
margin-right: 20rpx;
}
.search-item .content{
flex:1;
color: #666;
font-size: 28rpx;
}
.history-container{
margin-top: 20rpx;
}
.history-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.history-header .history-title{
font-size: 26rpx;
font-weight:550;
}
.history-header image{
width: 36rpx;
height: 36rpx;
}
.history-list{
display: flex;
flex-wrap: wrap;
padding: 20rpx 0;
}
.history-item{
font-size: 26rpx;
height: 36rpx;
line-height: 36rpx;
text-align: center;
padding: 6rpx 16rpx;
background: #eee;
border-radius: 16rpx;
margin: 0 20rpx 20rpx 0;
}
pages/search/search.js
const host = "http://localhost:3000"
let timer = null;
Page({
data:{
hotList:[],
keyword:'',
searchList:[],
historyList:[]
},
onLoad(){
this.getHotList();
const historyList = wx.getStorageSync('historyList');
if(historyList){
this.setData({
historyList})
}
},
getHotList(){
const result = [
{
id:"001",keyword:" Jay Chou ",iconUrl:host+"/images/hot-fill.png"},
{
id:"002",keyword:" The greatest work "},
{
id:"003",keyword:" Jj Lin "},
{
id:"004",keyword:" Lone brave "},
{
id:"005",keyword:" Goodbye, Monica "},
{
id:"006",keyword:" Eason Chan "},
{
id:"007",keyword:" Li Ronghao "},
{
id:"008",keyword:" Mao Bu Yi "}
]
this.setData({
hotList:result})
},
handleInput(event){
const keyword = event.detail.value.trim();
if(!keyword) {
this.setData({
keyword:''});
return;
}
this.throttle(this.getSearchList,500);
const {
historyList} = this.data;
const index = historyList.indexOf(keyword);
if(index > -1){
historyList.splice(index,1);
}
const newHistoryList = [keyword,...historyList].slice(0,10) // Display at most 10 Search history , And the latecomers live on
wx.setStorageSync("historyList",newHistoryList)
this.setData({
keyword,
historyList:newHistoryList
});
},
throttle(fn,delay){
if(timer != null) return;
timer = setTimeout(() => {
timer = null
fn();
},delay)
},
getSearchList(){
const result = [
{
id:"001",content:" Jay Chou "},
{
id:"002",content:" Jay Chou The greatest work "},
{
id:"003",content:" Jay Chou Grandpa's tea "},
{
id:"004",content:" Jay Chou Coral Sea "},
{
id:"005",content:" Jay Chou Chapter seven of the night "},
{
id:"006",content:" Jay Chou Don't cry "},
{
id:"007",content:" Jay Chou Wait for you to finish class "},
{
id:"008",content:" Jay Chou I believe so "}
]
this.setData({
searchList:result})
},
removeKeyword(){
this.setData({
keyword:'',searchList:[]})
},
deleteHistory(){
this.setData({
historyList:[]});
wx.removeStorageSync('historyList');
}
})
pages/login/login.json
{
"navigationBarTitleText": " Sign in "
}
pages/login/login.wxml
<view class="login-container">
<view class="list">
<view class="item">
<input type="text" data-type="username" bindinput="handleInput" placeholder=" user name " />
</view>
<view class="item">
<input type="text" password data-type="password" bindinput="handleInput" placeholder=" password " />
</view>
</view>
<button size="mini" bindtap="login"> Sign in </button>
</view>
pages/login/login.wxss
.login-container{
padding: 20rpx;
}
.list{
margin: 20rpx auto;
}
.item{
margin: 12rpx 0;
padding: 0 20rpx;
border: 1px solid #ddd;
border-radius: 6rpx;
}
.item input{
width: 100%;
height: 60rpx;
font-size: 28rpx;
}
button{
font-weight: normal;
font-size: 28rpx!important;
color: #fff;
background: #0149af;
}
pages/login/login.js
Page({
data:{
username:"",
password:""
},
handleInput(e){
const type = e.target.dataset.type;
this.setData({
[type]:e.detail.value
})
},
login(){
const {
username,password} = this.data;
if(!username){
wx.showToast({
title: ' Please enter your user name ',
icon:'error'
})
return;
}
if(!password){
wx.showToast({
title: ' Please enter your password ',
icon:'error'
})
return;
}
this.sendLoginRequest();
wx.showToast({
title: ' Login successful ',
success:() => {
setTimeout(() => {
wx.reLaunch({
url: '/music-package/pages/index/index',
})
},300)
}
})
},
sendLoginRequest(){
const userInfo = {
avatarUrl:"/static/images/avatar.png",
nickname:"Duck sir "
}
wx.setStorageSync("userInfo",JSON.stringify(userInfo));
}
})
to subcontract
music-package/pages/index/index.json
{
"usingComponents": {
},
"navigationBarTitleText": " music ",
"navigationBarBackgroundColor": "#624d2e",
"navigationBarTextStyle": "white"
}
music-package/pages/index/index.wxml
<view class="index-container">
<view class="header">
<image src="/static/images/icon-play-square.png"></image>
<text> Play all </text>
<text>{
{musicList.length}}</text>
</view>
<scroll-view class="music-list" enable-flex scroll-y >
<view class="music-item" wx:for="{
{musicList}}" wx:key="id" bindtap="handleTap" data-musicindex="{
{index}}" data-musicitem="{
{item}}">
<view class="music-index">{
{index+1}}</view>
<image class="music-image" src="{
{item.picUrl}}"></image>
<view class="music-info">
<view class="musci-name">{
{item.name}}</view>
<view class="music-author">{
{item.author}}</view>
</view>
<image class="btn-more" src="/static/images/icon-more.png"></image>
</view>
</scroll-view>
</view>
music-package/pages/index/index.wxss
.index-container{
padding: 20rpx;
background:#624d2e;
}
.header{
display: flex;
align-items: center;
}
.header image{
width: 28rpx;
height: 28rpx;
margin-right: 20rpx;
}
.header text{
color: #fff;
height: 28rpx;
line-height: 28rpx;
margin-right: 20rpx;
font-size: 28rpx;
}
.music-list{
margin-top: 20rpx;
color: #fff;
height: calc(100vh - 88rpx);
}
.music-item{
display: flex;
align-items: center;
height: 100rpx;
margin: 20rpx 0;
position: relative;
}
.music-item .music-index{
width: 50rpx;
font-size: 28rpx;
color: #aaa;
}
.music-item .music-image{
width: 80rpx;
height: 80rpx;
border-radius: 6rpx;
margin-right: 20rpx;
}
.music-item .music-info{
display: flex;
flex-direction: column;
}
.music-item .music-info .music-author{
font-size: 24rpx;
color: #aaa;
margin-top: 10rpx;
}
.music-item .btn-more{
width: 36rpx;
height: 36rpx;
position: absolute;
right: 0;
}
music-package/pages/index/index.js
import PubSub from "pubsub-js";
const host = "http://localhost:3000";
Page({
data:{
musicList:[], // Song list
musicindex:0 // After entering a song , Record the index of the song in the list
},
onLoad(){
this.getDataFromServer();
// receive messages , Receive from page pages/music/music The news of , according to “ On a ”or“ The following piece ”, Determine which song should be displayed currently
PubSub.subscribe("switchsong",(msgName,type) => {
// console.log(msgName,type);
let {
musicindex,musicList} = this.data;
if(type === "prev"){
if(musicindex===0) {
musicindex = musicList.length-1;
}else{
musicindex--;
}
}else if(type === "next"){
if(musicindex === musicList.length-1){
musicindex = 0;
}else{
musicindex++;
}
}
this.setData({
musicindex});
const music = musicList[musicindex];
// Send a message , Tell page pages/music/music, Tell it the details of the song after switching .
PubSub.publish("refreshmusic",music);
})
},
getDataFromServer(){
const result = [
{
id:"001",name:" In torrential rain ","author":" Li Ruoxi ","picUrl":host+"/images/ In torrential rain .jpg","url":host+"/audios/ In torrential rain .mp3",duration:161000},
{
id:"002",name:"Last Dance","author":" Five hundred ","picUrl":host+"/images/Last Dance.jpg","url":host+"/audios/Last Dance.mp3",duration:271000},
{
id:"003",name:" The king and the beggar ","author":" Hua Chenyu ","picUrl":host+"/images/ The king and the beggar .jpg","url":host+"/audios/ The king and the beggar .mp3",duration:178000},
{
id:"004",name:" Chilo Levis reply ","author":" Xue Kaiqi ","picUrl":host+"/images/ Chilo Levis reply .jpg","url":host+"/audios/ Chilo Levis reply .mp3",duration:243000},
{
id:"005",name:" Red sun ","author":" Li Keqin ","picUrl":host+"/images/ Red sun .jpg","url":host+"/audios/ Red sun .mp3",duration:291000},
{
id:"006",name:" Happy worship ","author":" Wilber Pan Zhang Shaohan ","picUrl":host+"/images/ Happy worship .jpg","url":host+"/audios/ Happy worship .mp3",duration:329000},
{
id:"007",name:" The door is unlocked ","author":" Huangpinguan ","picUrl":host+"/images/ The door is unlocked .jpg","url":host+"/audios/ The door is unlocked .mp3",duration:233000},
{
id:"008",name:" Just love you ","author":" David Tao ","picUrl":host+"/images/ Just love you .jpg","url":host+"/audios/ Just love you .mp3",duration:340000},
{
id:"009",name:" Happy little frog ","author":" Liu Shiming ","picUrl":host+"/images/ Happy little frog .jpg","url":host+"/audios/ Happy little frog .mp3",duration:130000},
{
id:"0010",name:" Years of friendship ","author":" Jordan Chan Zheng Yijian Thank God Lin Xiaofeng Qian Jiale ","picUrl":host+"/images/ Years of friendship .jpg","url":host+"/audios/ Years of friendship .mp3",duration:209000},
{
id:"0011",name:" gentle ","author":" May day ","picUrl":host+"/images/ gentle .jpg","url":host+"/audios/ gentle .mp3",duration:269000}
];
this.setData({
musicList:result});
},
handleTap(event){
const {
musicitem,musicindex} = event.currentTarget.dataset;
this.setData({
musicindex})
wx.navigateTo({
url: '/music-package/pages/music/music?musicitem='+JSON.stringify(musicitem),
})
}
})
music-package/pages/music/music.json
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2f434e",
"navigationBarTitleText": " Music details ",
"navigationBarTextStyle": "white"
}
music-package/pages/music/music.wxml
<view class="music-container">
<view class="music-name">{
{music.name}}</view>
<view class="music-author">{
{music.author}}</view>
<image class="arm {
{isPlay&&'arm-reset'}}" src="/static/images/arm.png"></image>
<view class="disc-container {
{isPlay&&'disc-animate'}}">
<image class="disc" src="/static/images/disc.png"></image>
<image class="music-image" src="{
{music.picUrl}}"></image>
</view>
<view class="progress-container">
<text class="current-time">{
{fmtCurrentTime}}</text>
<view class="bar-box">
<view class="current-progress" style="width:{
{
currentWidth}}rpx">
<view class="progress-circle"></view>
</view>
</view>
<text class="duration">{
{fmtDuration}}</text>
</view>
<view class="player">
<view class="btns">
<image class="loop-btn" src="/static/images/loop.png"></image>
<image class="prev-btn" src="/static/images/prev.png" id="prev" bindtap="handleSwitch"></image>
<image class="play-btn" src="{
{isPlay?'/static/images/stop.png':'/static/images/play.png'}}" bindtap="handlePlay"></image>
<image class="next-btn" src="/static/images/next.png" id="next" bindtap="handleSwitch"></image>
<image class="list-btn" src="/static/images/list.png"></image>
</view>
</view>
</view>
music-package/pages/music/music.wxss
.music-container{
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: #2f434e;
position: relative;
}
.music-container .music-name{
margin: 10rpx 0;
color: #fff;
font-size: 36rpx;
}
.music-container .music-author{
color: #bbb;
font-size: 28rpx;
margin: 6rpx 0;
}
.music-container .arm{
width:204rpx;
height: 358rpx;
position: relative;
left: 72rpx;
z-index: 99;
transform: rotate(-15deg);
transform-origin: 30rpx 30rpx;
transition: transform .7s linear;
}
.disc-container{
position: relative;
top: -128rpx;
width: 490rpx;
height: 490rpx;
}
.disc-container .disc{
width: 100%;
height: 100%;
}
.disc-container .music-image{
width: 270rpx;
height: 270rpx;
border-radius: 100%;
position: absolute;
left: 0;right: 0;top: 0;bottom: 0;
margin: auto;
}
.music-container .arm-reset{
transform: rotate(0deg);
}
.disc-animate{
animation: rotate 2.5s 1s linear infinite;
}
@keyframes rotate{
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
.player{
width: 100%;
position: absolute;
bottom: 60rpx;
}
.btns{
display: flex;
align-items: center;
justify-content: space-evenly;
}
.btns image{
width: 36rpx;
height: 36rpx;
}
.btns .play-btn,.btns .stop-btn{
width: 90rpx;
height: 90rpx;
}
.progress-container {
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
}
.progress-container .bar-box{
width: 456rpx;
height: 4rpx;
line-height: 4rpx;
background: #888;
}
.bar-box .current-progress{
position: relative;
height: 4rpx;
line-height: 4rpx;
background: #fff;
}
.progress-circle{
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: #fff;
position: absolute;
right: -16rpx;
top: -6rpx;
}
.progress-container text{
color: #ccc;
font-size: 22rpx;
margin: 0 40rpx;
}
music-package/pages/music/music.js
import PubSub from "pubsub-js";
import moment from 'moment';
const appInstance = getApp();
Page({
data:{
isPlay:false,
music:{
},
fmtDuration:"00:00",
fmtCurrentTime:"00:00",
currentWidth:0
},
onLoad(options){
const music = JSON.parse(options.musicitem);
let fmtDuration = moment(music.duration).format("mm:ss");
this.setData({
music,fmtDuration})
const {
isPlayGlobal,musicIdGlobal} = appInstance.globalData;
const {
id} = this.data.music;
if(isPlayGlobal && musicIdGlobal === id) {
this.setData({
isPlay:true});
}
this.bam = wx.getBackgroundAudioManager();
this.bam.onPlay(() => {
this.setData({
isPlay:true})
appInstance.globalData.isPlayGlobal = true;
appInstance.globalData.musicIdGlobal = this.data.music.id;
})
this.bam.onPause(() => {
this.setData({
isPlay:false})
appInstance.globalData.isPlayGlobal = false;
})
this.bam.onStop(() => {
this.setData({
isPlay:false})
appInstance.globalData.isPlayGlobal = false;
})
this.bam.onEnded(() => {
// this.setData({isPlay:false});
// appInstance.globalData.isPlayGlobal = false;
// The music naturally ends , Then switch to the next song
PubSub.publish("switchsong","next");
// receive messages , Receive from pages/index/index The news of , Show the song details after switching
const eventId = PubSub.subscribe("refreshmusic",(msgName,music) => {
PubSub.unsubscribe(eventId);
this.setData({
music})
this.musicControl();
const fmtDuration = moment(this.data.music.duration).format("mm:ss");
this.setData({
fmtDuration,fmtCurrentTime:"00:00"})
})
})
this.bam.onTimeUpdate(() => {
const currentWidth = Math.floor(456 * this.bam.currentTime / this.bam.duration);
const fmtCurrentTime = moment(this.bam.currentTime * 1000).format("mm:ss");
this.setData({
currentWidth,fmtCurrentTime});
})
},
handlePlay(){
const isPlay = !this.data.isPlay;
this.setData({
isPlay});
this.musicControl();
},
musicControl(){
const {
isPlay} = this.data;
if(isPlay){
this.bam.src = this.data.music.url;
this.bam.title = this.data.music.name;
}else{
this.bam.pause();
}
},
handleSwitch(event){
const type = event.target.id;
// Send a message , tell pages/index/index: Cut one or the next .prev Represents cutting a song ,next Represents cutting off a song .
PubSub.publish("switchsong",type);
// receive messages , Receive from pages/index/index The news of , Show the song details after switching
const eventId = PubSub.subscribe("refreshmusic",(msgName,music) => {
PubSub.unsubscribe(eventId);
this.setData({
music})
this.musicControl();
})
}
})
Related links
边栏推荐
猜你喜欢
随机推荐
阿里云 MSE 支持 Go 语言流量防护
[C语言刷题篇]链表运用讲解
[1331. Array serial number conversion]
prometheus配置alertmanager完整过程
同质化代币与 NFT 结合,如何使治理结构设计更灵活?
Introduction to redis II: RedHat 6.5 installation and use
About the title of linking to other pages
Pop up modal box
太空射击第15课: 道具
Sorting out problems in interface endpoint testing practice using Supertest
#yyds干货盘点# 面试必刷TOP101:链表中的节点每k个一组翻转
Character device drive structure
Shanghai Jiaotong University joined hands with Taobao to set up a media computing laboratory: promoting the development of key technologies such as video super score
The engineering practice of super large model was polished, and Baidu AI Cloud released the cloud native AI 2.0 solution
类与对象(中)
算法面试高频题解指南【一】
Establishment of flask static file service
About IP address
Redis的三种删除策略以及逐出算法
[pytorch] LSTM neural network





![Teach you how to draw a map with ArcGIS [thermal map]](/img/16/993da4678667884a98e1d82db37d69.png)



