NWP helper in GuangDong

广东省气象业务网数值预报页面修改

目前为 2018-12-03 提交的版本。查看 最新版本

// ==UserScript==
// @name        NWP helper in GuangDong
// @description 广东省气象业务网数值预报页面修改
// @namespace   minhill.com
// @include     http://10.148.8.228/to_fore_homepage.action*
// @version     1.3.5
// @grant       GM_addStyle
// @grant       @grant unsafeWindow
// @license     The MIT License (MIT); http://opensource.org/licenses/MIT
// @compatible  firefox
// @compatible  chrome
// @compatible  edge
// @note        2018/01/08 增加时效转换按钮
// @note        2018/01/15 增加经纬度功能
// @note        2018/12/03 增加显示JMA功能,暂时隐藏经纬度
// @supportURL  https://greasyfork.org/scripts/26259
// @author Hanchy Hill
// ==/UserScript==
// console.log($settings);
// @require https://cdn.bootcss.com/lodash.js/4.17.11/lodash.min.js
// select the target node
let userConfig = { // 用户设置
  alterDate : false, // 默认不修改时次
};

let $ = unsafeWindow.$;

const elemsConfig = {
  latLonInput: undefined,
  showJMA:false,
  $doc:()=>unsafeWindow.$doc,
  $settings:()=>unsafeWindow.$settings,
  $home:unsafeWindow.$home,
};

const helperConfig = {
  region:{
    hn:{//华南
      isMap: true,// 是否是等经纬地图
      projection:'Equidistant',// 投影
      latLon:{
        xRight:636.98, xLeft:172.56,
        yTop:56.516, yBottom:547.576,
        lon0:105.0, lon1:120.0,
        lat0:15.0, lat1:30.0,
      },
    },
    cn:{//中国
      isMap: true,// 是否是等经纬地图
      projection:'Lambert',
      latLon:{
        refLon0:110.25,refLat0:20,
        m0:342.98333,n0:382.51666,
        // refLon0:110,refLat0:20,
        // m0:338.98,n0:382.51,
        refLat1:1.001,refLat2:25.0,
        lonr : 80,latr :40,
        mr:94.983,nr:167.51,
      },

    },
    oy:{//欧亚
      isMap: true,// 是否是等经纬地图
      projection:'Mercator',
      latLon:{
        xRight:692.9833, xLeft:156.9833,
        yTop:54.51666, yBottom:533.51666,
        lon0:60.0, lon1:160.0,
        lat0:10.0, lat1:70.0,
      },
    },
    gd:{//广东
      isMap: true,// 是否是等经纬地图
      projection:'Equidistant',// 投影
      latLon:{
        xRight:589.98334, xLeft:124.98334,
        yTop:104.51666, yBottom:437.51666,
        lon0:110, lon1:116,
        lat0:21.0, lat1:25.0,
      },
    },
    hy:{//海洋
      isMap: true,// 是否是等经纬地图
      projection:'Lambert',// 投影
      latLon:{
        refLon0:110,refLat0:0,
        m0:274.983,n0:490.51666,
        refLat1:1.001,refLat2:25.0,
        lonr : 100,latr: 20,
        mr:171.983,nr:270.51666,
      },
    },
    '86st':{//单站
      isMap: false,
    },
    rainnest:{//雨涡
      isMap: false,
    },

  },
  currentRegion:'hn',
  matchLoc:()=>{},// 获取经纬度的函数
  matchParam:'',// 调用上式的第二参数
};


//
/**
 * unsafeWindow函数
 * selectProItem
 */

const utils = {
  changeRegion(region){
    helperConfig.currentRegion = region;
    return helperConfig.region[region].isMap;// 返回是否是地图
  },
  projection:{
    Mercator:{// 墨卡托投影
      calBasicInfo(lat1=0,lat2=0,n1=0,n2=0){
        /*参数lat 纬度, n坐标数值
        n0 赤道,d0放大系数
        */
        const y1 = Math.log(Math.tan(lat1)+1/Math.cos(lat1));
        const y2 = Math.log(Math.tan(lat2)+1/Math.cos(lat2));
        const n0 = (n1 - (y1/y2) * n2) / (1.0 - y1/y2);
        const d0 = y1/(n0 - n1);
        return {n0,d0};
      },
      calLatLon(mouseXY,dims){
        // console.log(dims);
        const n0 = dims.n0;
        const d0 = dims.d0;
        // console.log(Math.sinh((n0-mouseXY.y)*d0));
        const lat = Math.atan(Math.sinh((n0-mouseXY.y)*d0));
        ///---------------//
        const r = dims.xLeft;
        const o = dims.xRight;
        const s = (dims.lon1 - dims.lon0) / (o - r);
        const u = dims.lon0 + (mouseXY.x - r) * s;
        return {lat:lat*180/Math.PI,lon:u};
      },
    },
    Equidistant:{//等经纬度
      calBasicInfo(){
        return helperConfig.region[helperConfig.currentRegion].latLon
      },
      calLatLon(mouseXY,dims){
        // console.log(dims);
        const r = dims.xLeft;
        const o = dims.xRight;
        const i = dims.yTop;
        const l = dims.yBottom;
        const s = (dims.lon1 - dims.lon0) / (o - r); // o - r 内框宽度 -> s = lon/height
        const d = (dims.lat1 - dims.lat0) / (i - l);// i - l 内框高度  -> d = lat/width
        const u = dims.lon0 + (mouseXY.x - r) * s;
        const m = dims.lat1 + (mouseXY.y - i) * d;
        return {lat:m,lon:u};
      },

    },
    Lambert:{// 兰伯特投影
      calBasicInfo({refLon0,refLat0,m0,n0, refLat1,refLat2,lonr,latr,mr,nr}){
        /*
        refLat0,refLon0,m0,n0 参考纬度、经度,屏幕X坐标,Y坐标;
        屏幕坐标与实际坐标映射:
          x = (m-m0)*dx // dx为x方向比例系数
          y = (n0-n)*dy // dy为y方向比例系数,y方向屏幕坐标反向,所以取反
        refLat1,refLat2  2个平行纬度
        latr,lonr,mr,nr 选取的另外一个点的经纬度和屏幕坐标

        phi 纬度,lambda经度

         */
        const ang2rad = (x=0)=> Math.PI * x/180.0;
        const [phi0,phi1,phi2] = [ang2rad(refLat0),ang2rad(refLat1),ang2rad(refLat2)];
        //console.log(phi0,phi1,phi2);
        const lambda0 = ang2rad(refLon0);
        const [tan,cos,sin,pow,PI,ln] = [Math.tan,Math.cos,Math.sin,Math.pow,Math.PI,Math.log];
        const cot = (x=0)=> Math.cos(x)/Math.sin(x);// 余切
        const sec = (x=0)=> 1/Math.cos(x);// 正割

        const n = ln(cos(phi1)*sec(phi2))/ln(tan(0.25*PI+0.5*phi2)*cot(0.25*PI+0.5*phi1));
        const F = (cos(phi1)*pow(tan(0.25*PI+0.5*phi1),n))/n;
        // n,F常量参数
        let rho = (phi=0)=>F*pow(cot(0.25*PI+0.5*phi),n); // rho变量
        let rho0 = rho(phi0);
        let fx = (phi,lambda)=>rho(phi)*sin(n*(lambda-lambda0));
        let fy = (phi,lambda)=>rho0 - rho(phi)*cos(n*(lambda-lambda0));
        const dx = fx(ang2rad(latr),ang2rad(lonr))/(mr-m0);
        const dy = fy(ang2rad(latr),ang2rad(lonr))/(n0-nr);
        // console.log(dx,dy);
        return {dx, dy, F, n, rho0, lambda0, m0, n0};
      },
      calLatLon(mouseXY,{dx,dy,F,n,rho0, lambda0, m0,n0}){
        const x = (mouseXY.x - m0)*dx;
        const y = (n0-mouseXY.y)*dy;

        let rho = (x,y)=>Math.sign(n)*Math.sqrt(Math.pow(x,2)+Math.pow((rho0-y),2));

        let theta = (x,y)=>Math.atan(x/(rho0-y));
        let phi = (rho)=>2*Math.atan(Math.pow(F/rho,1/n))-0.5*Math.PI;
        let lambda = (theta)=>lambda0 + theta/n;
        const lat = phi(rho(x,y))*180/Math.PI;
        const lon = lambda(theta(x,y))*180/Math.PI;
        // console.log(mouseXY.x-m0,n0-mouseXY.y);
        //console.log(mouseXY.x - m0,n0-mouseXY.y);
        return {lat,lon};
      },
    }

  },
  debounce:(fn, delay, scope)=>{
    let timer = null;
    // 返回函数对debounce作用域形成闭包
    return function () {
        // setTimeout()中用到函数环境总是window,故需要当前环境的副本;
        let context = scope || this, args = arguments;
        // 如果事件被触发,清除timer并重新开始计时
        clearTimeout(timer);
        timer = setTimeout(function () {
            fn.apply(context, args);
        }, delay);
    }
  },
  throttle(fn, threshold, scope) {
      let timer;
      let prev = Date.now();
      return function () {
          let context = scope || this, args = arguments;
          let now = Date.now();
          if (now - prev > threshold) {
              prev = now;
              fn.apply(context, args);
          }
      }
  },
};


var NWP_init = function(){
    var fcHour = document.getElementById('forecast_hour');
    var iniTime = document.getElementById('create_day');
    var infoBar = document.getElementById('pic_info');
    var referNode = document.getElementById('to_contrast');
    var divTime = document.createElement('span');
    divTime.textContent = 'hello world';
    divTime.setAttribute('class', 'lcTime');
    divTime.style.position = 'relative';
    divTime.style.float = 'right';
    divTime.style.right = '120px';
    infoBar.insertBefore(divTime, referNode);
    // document.querySelector("#forecast_hours div").textContent = "日期";

    // create an observer instance
    var UTC8 = new MutationObserver(function (mutations) {
      var dateString = iniTime.textContent.match(/(\d+).*?(\d+).*?(\d+).*?(\d+)/);
      var fcDate = [];
      fcDate[0] = Number(dateString[1]);
      fcDate[1] = Number(dateString[2]);
      fcDate[2] = Number(dateString[3]);
      fcDate[3] = Number(dateString[4]);
      fcDate[4] = Number(fcHour.textContent.match(/\d+/));
      fcDate[5] = new Date(fcDate[0], fcDate[1] - 1, fcDate[2], fcDate[3] + fcDate[4] + 8);
      var localTime = String(fcDate[5].getMonth() + 1) + '月' + fcDate[5].getDate() +
      '日' + fcDate[5].getHours() + '时 GMT+8';
      divTime.textContent = localTime;
    });
    // configuration of the observer:
    var config = {
      attributes: true,
      childList: true,
      characterData: true
    };
    UTC8.observe(fcHour, config);
    // later, you can stop observing
    //observer.disconnect();
    //
    //
    /////////////////////////////////////////////////////////////


    //
    ///
    ////////////////////修改时效列/////////////////////////////////////////
    var alterTimelist = function (mutations) {
      //alert(timeBar.length);
      if(!userConfig.alterDate) return; // 不修改则直接返回
      var dateString = iniTime.textContent.match(/(\d+).*?(\d+).*?(\d+).*?(\d+)/);
      var fcDate = [];
        fcDate[0] = Number(dateString[1]);
        fcDate[1] = Number(dateString[2]);
        fcDate[2] = Number(dateString[3]);
        fcDate[3] = Number(dateString[4]);
      for (let i = 0; i < timeBar.length; i++) {
        let oValue = timeBar[i].value;
        fcDate[4] = Number(timeBar[i].value);

        fcDate[5] = new Date(fcDate[0], fcDate[1] - 1, fcDate[2], fcDate[3] + fcDate[4] + 8);

        let iday = String(fcDate[5].getDate());
        iday = Array(2 > iday.length ? 2 - iday.length + 1 || 0 : 0).join(0) + iday;

        let ihour = String(fcDate[5].getHours());
        ihour = Array(2 > ihour.length ? 2 - ihour.length + 1 || 0 : 0).join(0) + ihour;

        let localTime = iday+' ' + ihour+'     ;';
        let styleText = '#'+timeBar[i].getAttribute("id")+':before{white-space:pre;content: "  '+localTime+'  "}';
        GM_addStyle(styleText);

        switch(fcDate[5].getHours()){
          case 5:
            timeBar[i].style.cssText = "border-left:2px solid #9B30FF";
            break;
          case 14:
            timeBar[i].style.cssText = "border-left:2px solid #EE3B3B";
            break;
          case 20:
            timeBar[i].style.cssText = "border-bottom:1px dotted #8E8E8E;border-left:2px solid #ffffff;";
            break;
          default:
            timeBar[i].style.cssText = "border-left:2px solid #ffffff;";
        }
      }
    };
    /////////////////////////////////////////////////////////////
    ///
    var selectObserver = new MutationObserver(alterTimelist);
    // configuration of the observer:
    var timeBar = document.querySelector("#forecast_hours select");
    var config2 = {
      attributes: false,
      childList: true,
      characterData: false,
    };
    selectObserver.observe(timeBar, config2);
    GM_addStyle("#forecast_hours option{width: 50px!important; overflow: hidden!important;}");

    //-----------------------------------------------------------------------------//
    let imgObserver = new MutationObserver(fitImgLoc);
    imgObserver.observe(timeBar, config2);
    //-----------------------------------绑定坐标-----------------------------------//
    function fitImgLoc(){ // 绑定img包含的div元素
      // console.log(unsafeWindow._region);
      // TODO
      const isMap = utils.changeRegion(unsafeWindow._region); // 改变地图;
      if(!isMap) return; // 如果不是地图直接返回
      /////TODO 判断地图逻辑分离
      const currMap = helperConfig.region[helperConfig.currentRegion];
      const currProjection = currMap.projection;
      //let matchLoc = ({});
      //let param = ({});
      switch (currProjection) {
        case 'Mercator':{const dims = currMap.latLon;
          const lat1 = Math.PI/180.0 * dims.lat0;
          const lat2 = Math.PI/180.0 *dims.lat1;
          const n1 = dims.yBottom;
          const n2 = dims.yTop;
          // console.log(dims);
          const param1 = utils.projection.Mercator.calBasicInfo(
            lat1, lat2, n1, n2
          );
          helperConfig.matchParam = {...param1,
                  xRight:dims.xRight,
                  xLeft:dims.xLeft,
                  lon0:dims.lon0,
                  lon1:dims.lon1,
                  };
          // console.log(helperConfig.matchParam);
          helperConfig.matchLoc = utils.projection.Mercator.calLatLon;
          break;
         }
        case 'Lambert':{
          const LamDim = currMap.latLon;
/*           const LamParam1 =
                [ LamDim.refLon0, LamDim.refLat0, LamDim.m0, LamDim.n0,
                  LamDim.refLat1, LamDim.refLat2,
                  LamDim.lonr, LamDim.latr, LamDim.mr, LamDim.nr]; */
          const LamParam2 = utils.projection.Lambert.calBasicInfo(LamDim);
          // console.log(LamParam2);
          helperConfig.matchParam = LamParam2;
          helperConfig.matchLoc = utils.projection.Lambert.calLatLon;
          break;
        }
        default:
          helperConfig.matchParam = currMap.latLon;
          helperConfig.matchLoc = utils.projection.Equidistant.calLatLon;
          break;
      }

      let wrapDiv = document.querySelector('#pic_frame div');
      if(wrapDiv){
        wrapDiv.addEventListener('mousemove', utils.debounce(getMouseLatLon,100));
        // console.log(wrapDiv.clientWidth);// offsetWidth , clientWidth, scrollWidth
      }
    }

     function getElemRelPos (e, t, n) {// e.target, e.clientX, e.clientY
      var a = e.getBoundingClientRect(),
          r = getComputedStyle(e);
      return {
          x: t - (a.left + parseFloat(r.paddingLeft) + parseFloat(r.borderLeftWidth)),
          y: n - (a.top + parseFloat(r.paddingTop) + parseFloat(r.borderTopWidth))
      };
    }

    function getMouseLatLon(event){
      let target = event.target;//
      //let dims = helperConfig.region[helperConfig.currentRegion].latLon;
      /*let dims =  {"width": 875, "height": 601,
                "pagewidth": 876, "pageheight": 601,
                "Xplot0": 211.0, "Xplot1": 812.0,
                "Yplot0": 53, "Yplot1": 676,// 从下往上算
                "lon0": 105.0, "lon1": 120.0,
                "lat0": 15.0, "lat1": 30.0}; */
                  //"pagewidth": 876, "pageheight": 602,
                  //"Xplot0": 87, "Xplot1": 773,
                  //"Yplot0": 54, "Yplot1": 549,
                  //"lon0": 101, "lon1": 126.314,
                  //"lat0": 15, "lat1": 30.15};
      const mouseXY = getElemRelPos(target, event.clientX, event.clientY); // 相对图像的像素位置{x,y}
      // console.log(mouseXY);
      // const r = dims.Xplot0 / dims.pagewidth * target.clientWidth;// r ->x右值
      // const o = dims.Xplot1 / dims.pagewidth * target.clientWidth;// o ->x左值
      // const i = (dims.pageheight - dims.Yplot1) / dims.pageheight * target.clientHeight;// y 上值
      // const l = (dims.pageheight - dims.Yplot0) / dims.pageheight * target.clientHeight;// y 下值
/*       const r = dims.xLeft;
      const o = dims.xRight;
      const i = dims.yTop;
      const l = dims.yBottom;
      const s = (dims.lon1 - dims.lon0) / (o - r); // o - r 内框宽度 -> s = lon/height
      const d = (dims.lat1 - dims.lat0) / (i - l);// i - l 内框高度  -> d = lat/width
      const u = dims.lon0 + (mouseXY.x - r) * s;
      const m = dims.lat1 + (mouseXY.y - i) * d; */
      const loc = helperConfig.matchLoc(mouseXY, helperConfig.matchParam);
      //elemsConfig.latLonInput.lat.innerHTML = mouseXY.x//loc.lat;
      // elemsConfig.latLonInput.lon.innerHTML = mouseXY.y//loc.lon;
      elemsConfig.latLonInput.lat.innerHTML = loc.lat;
      elemsConfig.latLonInput.lon.innerHTML = loc.lon;
      // console.log(mouseXY.x, mouseXY.y);
      // console.log(mouseXY.x, mouseXY.y);
      //console.log(m,u,mouseXY.x,mouseXY.y);
      return {lat: loc.lat, lon: loc.lon};

    }
    //------------------------------24小时跳跃-------------------------------------//
    const timeJump = function(){
      //var hourBar = document.getElementById('from_hour');float-l
      var jumpParent = document.querySelector('.float-l');
      var pre24 = document.createElement('button');
      pre24.addEventListener("click", function(){timeTrigger(-24);});
      pre24.textContent = "-24";
      jumpParent.appendChild(pre24);

      var next24 = document.createElement('button');
      next24.addEventListener("click", function(){timeTrigger(24);});
      next24.textContent = "+24";
      jumpParent.appendChild(next24);

      var timeTrigger = function(timer){
        let selectedVal = timeBar[timeBar.selectedIndex].getAttribute("data-hour");
        let nextVal = String(Number(selectedVal) + timer);
        var posi = 3;
        nextVal = Array(posi > nextVal.length ? posi - nextVal.length + 1 || 0 : 0).join(0) + nextVal;
        let nextopt = timeBar.querySelector("#option_"+nextVal);
        //alert(nextopt);
        if(!nextopt) return;
        timeBar[timeBar.selectedIndex].selected = false;
        nextopt.selected = true;
        //var oitem = document.getElementById('option_018');
        //oitem.selected = true;
        var changeEvt = document.createEvent('HTMLEvents');
        changeEvt.initEvent('change',true,true);
        timeBar.dispatchEvent(changeEvt);
      };
    };

    timeJump();
    /////切换时效

    function switchDate(){
      userConfig.alterDate = !userConfig.alterDate;
      if(userConfig.alterDate){
        switchDateBtn.textContent = "切换成时效";
        alterTimelist();
      }
      else{
        switchDateBtn.textContent = "切换成日期";
        for(let ele of timeBar){
          ele.style.cssText = '';
          let styleText = '#'+ele.getAttribute("id")+':before{white-space:pre;content: ""}';
        GM_addStyle(styleText);
        }
      }
    }

    var switchParent = document.querySelector('.float-l');
    let switchDateBtn = document.createElement('button');
    switchDateBtn.addEventListener("click", switchDate);
    switchDateBtn.textContent = "切换成日期";
    switchParent.appendChild(switchDateBtn);
    /////end 切换时效 /////
  };

function showJMA(){
  var getXML = function(_url, callback, errorcall){
  return $.ajax({
   type: "GET",
   url: _url,
   dataType: "text",
   success: function(xml){
        var alterString = xml.replace(/<!-- <Products name="jmathin_d"/,'<Products name="jmathin_d"')
                             .replace(/<\/Products>-->\n\s+<Pro.*?专业/,`</Products><Products comparable="flase" show="true" item="专业`);
                             //.replace(/<!--<Products name="ecmwf_b"/,`<Products name="ecmwf_b"`)
                             //.replace(/<\/Products>-->\n\s+<Pro.*?ec/,`</Products><Products name="ec`);
        var parser = new DOMParser();
        var xmlDoc = parser.parseFromString(alterString, "text/xml");
    var $xml = $(xmlDoc);

    callback($xml);
   },
   error: function(XMLHttpRequest, textStatus, errorThrown)
   {
    $.holdReady(false);
    if(errorcall && $.isFunction(errorcall)) errorcall(XMLHttpRequest, textStatus, errorThrown);
   }
  });
 };
// 加载XML配置文件

/**
 * 加载左侧菜单
 */
  function reloadMenu(){
    document.getElementById('items').innerHTML = '';
    //doSetLeftMenu();
   let products=[];
   var span = '<span class="lm_item lm_item_ex lm_item_ep"></span>';
   var titleSpan='<a class="lm_item_a"></a>'
   // 添加对比菜单项
   unsafeWindow.$doc.find('Comparison[show="true"]').each(
     function(i, pros) {
      $(span).attr("id", $(pros).attr("name")).attr("name",
        $(pros).attr("item")).text($(pros).attr("item")).click(
        function() {
         unsafeWindow.doComparClick(this.id);
        }).appendTo($("#leftMenu #items")).attr("title",$(pros).attr("item"))
        .addClass("lm_item_a").removeClass('lm_item_ep')
        .css({'height':'30px','line-height':'30px','font-size':'14px','text-indent':'8px','text-align':'left','cursor': 'pointer'});
     });

      // 添加预报模式
 //var o = $doc.find('Products[show="true"]');
    var options="";
    unsafeWindow.$doc.find('Products[show="true"]').each(
      function(i, pros) {
       if(!$(pros).attr("name")){
        var ts = $(titleSpan).text($(pros).attr("item")).appendTo($("#leftMenu #items")).attr("title",$(pros).attr("item"));
        if($(pros).attr("close") == "true"){
         ts.addClass("menu-close");
        }
        return;
       }
       var formatItem=$(pros).attr("item");
       options+="<option value='"+formatItem+"'>"+formatItem+"</option>";
       $(span).attr("id", $(pros).attr("name")).attr("name",
         $(pros).attr("item")).attr(
         "path",
         undefined == ($(pros).attr("path")) ? null : $(pros)
           .attr("path")).text($(pros).attr("item"))
         .click(
           function() {
            unsafeWindow.doClickPors(this.id);
           }).appendTo($("#leftMenu #items")).attr("title",$(pros).attr("item"));
      });
    // console.log(options);
    var impLabWeb = "<a id='rdszdsys'"+
     "href='http://www.grapes-trams.org.cn/Products.aspx?mode=1'"+
     " target='_blank' "+
     " style=' width: 104px;  height: 32px; line-height: 32px; text-align: center; background: none repeat scroll 0 0 #3381CF; color: #FFFFFF; z-index: 10;display:block'>重点实验室外网</a>"
     $("#leftMenu #items").append(impLabWeb);
     $('#jmathin_d').click()
  };

  getXML(unsafeWindow.$home + '/data/forecastInfo.xml', function($xml) {

    unsafeWindow.$doc = $xml;
    reloadMenu();
    }, function() {
   alert("加载XML配置文件失败!");
  });
  elemsConfig.showJMA = !elemsConfig.showJMA;
}
function createPanel(){
  //-创建面板-//
  const panelWrap = document.createElement("div");

  panelWrap.setAttribute("id","helper_panel");
  panelWrap.setAttribute("class","show_panel");

  /* 设置添加JMA*/
  const addJMA = document.createElement('div');
  addJMA.innerHTML = `<div id="show-jma" class="jma-button not-active">显示JMA</div>`;
  panelWrap.appendChild(addJMA);
  addJMA.addEventListener('click', showJMA);
  /* 设置Lat lon面板*/
  const latLonWarp = document.createElement("div");
  latLonWarp.setAttribute("id","helper_latLon");
  latLonWarp.setAttribute("class","show_latLon");
  latLonWarp.innerHTML = '<span>Lat <span class="fixLoc" id="helper_lat"></span> Lon <span class="fixLoc" id="helper_lon"></span></span>';
  panelWrap.appendChild(latLonWarp);

  const ibody = document.getElementsByTagName("body")[0];
  ibody.appendChild(panelWrap);
  //-------//
  //-注册到全局变量-//
  elemsConfig.latLonInput = {
    lat:document.getElementById('helper_lat'),
    lon:document.getElementById('helper_lon'),
  };
}
// 添加面板样式

GM_addStyle(`.show_panel{display:block!important;z-index:1001;}
  #helper_panel{
      position:absolute; top:5px;right:500px;
      background-color: rgb(45,53,63);
      border-bottom: 0px solid rgb(20,20,20); padding:5px;
      border-bottom: 0px solid rgb(20,20,20);border-radius: 4px;border: 1px solid rgb(22,25,28);
      box-shadow:0 1px 0 rgba(162,184,204,0.25) inset,0 0 4px hsla(0,0%,0%,0.95);
      color: white;}
  .jma-button{
    cursor:pointer;
  }
    `);
/**暂时隐藏按钮 */
// GM_addStyle(`#helper_latLon .fixLoc{display:inline-block;width:40px;height:15px;overflow:hidden;}`);
GM_addStyle(`#helper_latLon{display:none;}`);
/**重要添加十字鼠标 */
// GM_addStyle('#pic_frame div{border:1px solid !important;cursor:crosshair !important;}');
createPanel();
NWP_init();